TeensyDmaAdcLogger.ino 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. // Test of Teensy exFAT DMA ADC logger.
  2. // This is mainly to test use of RingBuf in an ISR.
  3. // You should modify it for serious use as a data logger.
  4. //
  5. #include "DMAChannel.h"
  6. #include "SdFat.h"
  7. #include "FreeStack.h"
  8. #include "RingBuf.h"
  9. // 400 sector RingBuf - could be larger on Teensy 4.1.
  10. const size_t RING_BUF_SIZE = 400*512;
  11. // Preallocate 8GiB file.
  12. const uint64_t PRE_ALLOCATE_SIZE = 8ULL << 30;
  13. // Use FIFO SDIO.
  14. #define SD_CONFIG SdioConfig(FIFO_SDIO)
  15. DMAChannel dma(true);
  16. SdFs sd;
  17. FsFile file;
  18. //------------------------------------------------------------------------------
  19. // Ping-pong DMA buffer.
  20. DMAMEM static uint16_t __attribute__((aligned(32))) dmaBuf[2][256];
  21. size_t dmaCount;
  22. // RingBuf for 512 byte sectors.
  23. RingBuf<FsFile, RING_BUF_SIZE> rb;
  24. // Shared between ISR and background.
  25. volatile size_t maxBytesUsed;
  26. volatile bool overrun;
  27. //------------------------------------------------------------------------------
  28. //ISR.
  29. static void isr() {
  30. if (rb.bytesFreeIsr() >= 512 && !overrun) {
  31. rb.memcpyIn(dmaBuf[dmaCount & 1], 512);
  32. dmaCount++;
  33. if (rb.bytesUsed() > maxBytesUsed) {
  34. maxBytesUsed = rb.bytesUsed();
  35. }
  36. } else {
  37. overrun = true;
  38. }
  39. dma.clearComplete();
  40. dma.clearInterrupt();
  41. #if defined(__IMXRT1062__)
  42. // Handle clear interrupt glitch in Teensy 4.x!
  43. asm("DSB");
  44. #endif // defined(__IMXRT1062__)
  45. }
  46. //------------------------------------------------------------------------------
  47. // Over-clocking will degrade quality - use only for stress testing.
  48. void overclock() {
  49. #if defined(__IMXRT1062__) // Teensy 4.0
  50. ADC1_CFG =
  51. // High Speed Configuration
  52. ADC_CFG_ADHSC |
  53. // Sample period 3 clocks
  54. ADC_CFG_ADSTS(0) |
  55. // Input clock
  56. ADC_CFG_ADIV(0) |
  57. // Not selected - Long Sample Time Configuration
  58. // ADC_CFG_ADLSMP |
  59. // 12-bit
  60. ADC_CFG_MODE(2) |
  61. // Asynchronous clock
  62. ADC_CFG_ADICLK(3);
  63. #else // defined(__IMXRT1062__)
  64. // Set 12 bit mode and max over-clock
  65. ADC0_CFG1 =
  66. // Clock divide select, 0=direct, 1=div2, 2=div4, 3=div8
  67. ADC_CFG1_ADIV(0) |
  68. // Sample time configuration, 0=Short, 1=Long
  69. // ADC_CFG1_ADLSMP |
  70. // Conversion mode, 0=8 bit, 1=12 bit, 2=10 bit, 3=16 bit
  71. ADC_CFG1_MODE(1) |
  72. // Input clock, 0=bus, 1=bus/2, 2=OSCERCLK, 3=async
  73. ADC_CFG1_ADICLK(0);
  74. ADC0_CFG2 = ADC_CFG2_MUXSEL | ADC_CFG2_ADLSTS(3);
  75. #endif // defined(__IMXRT1062__)
  76. }
  77. //------------------------------------------------------------------------------
  78. #if defined(__IMXRT1062__) // Teensy 4.0
  79. #define SOURCE_SADDR ADC1_R0
  80. #define SOURCE_EVENT DMAMUX_SOURCE_ADC1
  81. #else
  82. #define SOURCE_SADDR ADC0_RA
  83. #define SOURCE_EVENT DMAMUX_SOURCE_ADC0
  84. #endif
  85. //------------------------------------------------------------------------------
  86. // Should replace ADC stuff with calls to Teensy ADC library.
  87. // https://github.com/pedvide/ADC
  88. static void init(uint8_t pin) {
  89. uint32_t adch;
  90. uint32_t i, sum = 0;
  91. // Actually, do many normal reads, to start with a nice DC level
  92. for (i=0; i < 1024; i++) {
  93. sum += analogRead(pin);
  94. }
  95. #if defined(__IMXRT1062__) // Teensy 4.0
  96. // save channel
  97. adch = ADC1_HC0 & 0x1F;
  98. // Continuous conversion , DMA enable
  99. ADC1_GC = ADC_GC_ADCO | ADC_GC_DMAEN;
  100. // start conversion
  101. ADC1_HC0 = adch;
  102. #else // defined(__IMXRT1062__) // Teensy 4.0
  103. // save channel
  104. adch = ADC0_SC1A & 0x1F;
  105. // DMA enable
  106. ADC0_SC2 |= ADC_SC2_DMAEN;
  107. // Continuous conversion enable
  108. ADC0_SC3 = ADC_SC3_ADCO;
  109. // Start ADC
  110. ADC0_SC1A = adch;
  111. #endif // defined(__IMXRT1062__) // Teensy 4.0
  112. // set up a DMA channel to store the ADC data
  113. dma.attachInterrupt(isr);
  114. dma.begin();
  115. dma.source((volatile const signed short &)SOURCE_SADDR);
  116. dma.destinationBuffer((volatile uint16_t*)dmaBuf, sizeof(dmaBuf));
  117. dma.interruptAtHalf();
  118. dma.interruptAtCompletion();
  119. dma.triggerAtHardwareEvent(SOURCE_EVENT);
  120. dma.enable();
  121. }
  122. //------------------------------------------------------------------------------
  123. void stopDma() {
  124. #if defined(__IMXRT1062__) // Teensy 4.0
  125. ADC1_GC = 0;
  126. #else // defined(__IMXRT1062__)
  127. ADC0_SC3 = 0;
  128. #endif // defined(__IMXRT1062__)
  129. dma.disable();
  130. }
  131. //------------------------------------------------------------------------------
  132. void printTest(Print* pr) {
  133. if (file.fileSize() < 1024*2) {
  134. return;
  135. }
  136. file.rewind();
  137. rb.begin(&file);
  138. // Could readIn RING_BUF_SIZE bytes and write to a csv file in a loop.
  139. if (rb.readIn(2048) != 2048) {
  140. sd.errorHalt("rb.readIn failed");
  141. }
  142. uint16_t data;
  143. for (size_t i = 0; i < 1024; i++) {
  144. pr->print(i);
  145. pr->print(',');
  146. rb.memcpyOut(&data, 2);
  147. pr->println(data);
  148. }
  149. }
  150. //------------------------------------------------------------------------------
  151. void runTest(uint8_t pin) {
  152. dmaCount = 0;
  153. maxBytesUsed = 0;
  154. overrun = false;
  155. do {
  156. delay(10);
  157. } while (Serial.read() >= 0);
  158. if (!file.open("IsrLoggerTest.bin", O_CREAT | O_TRUNC | O_RDWR)) {
  159. sd.errorHalt("file.open failed");
  160. }
  161. if (!file.preAllocate(PRE_ALLOCATE_SIZE)) {
  162. sd.errorHalt("file.preAllocate failed");
  163. }
  164. rb.begin(&file);
  165. Serial.println("Type any character to stop\n");
  166. init(pin);
  167. uint32_t samplingTime = micros();
  168. while (!overrun && !Serial.available()) {
  169. size_t n = rb.bytesUsed();
  170. if ((n + file.curPosition()) >= (PRE_ALLOCATE_SIZE - 512)) {
  171. Serial.println("File full - stopping");
  172. break;
  173. }
  174. if (n >= 512) {
  175. if (rb.writeOut(512) != 512) {
  176. Serial.println("writeOut() failed");
  177. file.close();
  178. return;
  179. }
  180. }
  181. }
  182. stopDma();
  183. samplingTime = (micros() - samplingTime);
  184. if (!file.truncate()) {
  185. sd.errorHalt("truncate failed");
  186. }
  187. if (overrun) {
  188. Serial.println("Overrun ERROR!!");
  189. }
  190. Serial.print("RingBufSize ");
  191. Serial.println(RING_BUF_SIZE);
  192. Serial.print("maxBytesUsed ");
  193. Serial.println(maxBytesUsed);
  194. Serial.print("fileSize ");
  195. Serial.println((uint32_t)file.fileSize());
  196. Serial.print(0.000001*samplingTime);
  197. Serial.println(" seconds");
  198. Serial.print(1.0*file.fileSize()/samplingTime, 3);
  199. Serial.println(" MB/sec\n");
  200. printTest(&Serial);
  201. file.close();
  202. }
  203. //------------------------------------------------------------------------------
  204. void waitSerial(const char* msg) {
  205. uint32_t m = micros();
  206. do {
  207. if (Serial.read() >= 0) {
  208. m = micros();
  209. }
  210. } while (micros() - m < 10000);
  211. Serial.println(msg);
  212. while (!Serial.available()) {}
  213. Serial.println();
  214. }
  215. //------------------------------------------------------------------------------
  216. void setup() {
  217. Serial.begin(9600);
  218. while (!Serial) {
  219. yield();
  220. }
  221. waitSerial("Type any character to begin");
  222. Serial.print("FreeStack: ");
  223. Serial.println(FreeStack());
  224. }
  225. //------------------------------------------------------------------------------
  226. void loop() {
  227. if (!sd.begin(SD_CONFIG)) {
  228. sd.initErrorHalt(&Serial);
  229. }
  230. //analogReadAveraging(1);
  231. //analogReadResolution(12);
  232. //overclock(); // 3 Msps on Teensy 3.6 - requires high quality card.
  233. runTest(A0);
  234. waitSerial("Type any character to run test again");
  235. }