PlatformIO package of the Teensy core framework compatible with GCC 10 & C++20
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

281 lines
10KB

  1. /* Teensy 3.x, LC, 4.0 ADC library
  2. https://github.com/pedvide/ADC
  3. Copyright (c) 2020 Pedro Villanueva
  4. Permission is hereby granted, free of charge, to any person obtaining
  5. a copy of this software and associated documentation files (the
  6. "Software"), to deal in the Software without restriction, including
  7. without limitation the rights to use, copy, modify, merge, publish,
  8. distribute, sublicense, and/or sell copies of the Software, and to
  9. permit persons to whom the Software is furnished to do so, subject to
  10. the following conditions:
  11. The above copyright notice and this permission notice shall be
  12. included in all copies or substantial portions of the Software.
  13. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  14. EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  15. MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  16. NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
  17. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
  18. ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  19. CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  20. SOFTWARE.
  21. */
  22. #ifdef ADC_USE_DMA
  23. #include "AnalogBufferDMA.h"
  24. //#define DEBUG_DUMP_DATA
  25. // Global objects
  26. AnalogBufferDMA *AnalogBufferDMA::_activeObjectPerADC[2] = {nullptr, nullptr};
  27. #if defined(__IMXRT1062__) // Teensy 4.0
  28. #define SOURCE_ADC_0 ADC1_R0
  29. #define DMAMUX_ADC_0 DMAMUX_SOURCE_ADC1
  30. #define SOURCE_ADC_1 ADC2_R0
  31. #define DMAMUX_ADC_1 DMAMUX_SOURCE_ADC2
  32. #elif defined(KINETISK)
  33. #define SOURCE_ADC_0 ADC0_RA
  34. #define DMAMUX_ADC_0 DMAMUX_SOURCE_ADC0
  35. #ifdef ADC_DUAL_ADCS
  36. #define SOURCE_ADC_1 ADC1_RA
  37. #define DMAMUX_ADC_1 DMAMUX_SOURCE_ADC1
  38. #endif
  39. #elif defined(KINETISL)
  40. #define SOURCE_ADC_0 ADC0_RA
  41. #define DMAMUX_ADC_0 DMAMUX_SOURCE_ADC0
  42. #endif
  43. //=============================================================================
  44. // Debug support
  45. //=============================================================================
  46. #ifdef DEBUG_DUMP_DATA
  47. static void dumpDMA_TCD(DMABaseClass *dmabc)
  48. {
  49. #ifndef KINETISL
  50. Serial.printf("%x %x:", (uint32_t)dmabc, (uint32_t)dmabc->TCD);
  51. Serial.printf("SA:%x SO:%d AT:%x NB:%x SL:%d DA:%x DO: %d CI:%x DL:%x CS:%x BI:%x\n", (uint32_t)dmabc->TCD->SADDR,
  52. dmabc->TCD->SOFF, dmabc->TCD->ATTR, dmabc->TCD->NBYTES, dmabc->TCD->SLAST, (uint32_t)dmabc->TCD->DADDR,
  53. dmabc->TCD->DOFF, dmabc->TCD->CITER, dmabc->TCD->DLASTSGA, dmabc->TCD->CSR, dmabc->TCD->BITER);
  54. #else
  55. Serial.printf("%x %x:", (uint32_t)dmabc, (uint32_t)dmabc->CFG);
  56. Serial.printf("SAR:%x DAR:%x DSR_BCR:%x DCR:%x\n", (uint32_t)dmabc->CFG->SAR, dmabc->CFG->DAR, dmabc->CFG->DSR_BCR,
  57. dmabc->CFG->DCR);
  58. #endif
  59. }
  60. #endif
  61. //=============================================================================
  62. // Init - Initialize the object including setup DMA structures
  63. //=============================================================================
  64. void AnalogBufferDMA::init(ADC *adc, int8_t adc_num)
  65. {
  66. // enable DMA and interrupts
  67. #ifdef DEBUG_DUMP_DATA
  68. Serial.println("AnalogBufferDMA::init");
  69. Serial.flush();
  70. #endif
  71. #ifndef KINETISL
  72. // setup a DMA Channel.
  73. // Now lets see the different things that RingbufferDMA setup for us before
  74. // See if we were created with one or two buffers. If one assume we stop on completion, else assume continuous.
  75. if (_buffer2 && _buffer2_count)
  76. {
  77. #ifdef ADC_DUAL_ADCS
  78. _dmasettings_adc[0].source((volatile uint16_t &)((adc_num == 1) ? SOURCE_ADC_1 : SOURCE_ADC_0));
  79. #else
  80. _dmasettings_adc[0].source((volatile uint16_t &)(SOURCE_ADC_0));
  81. #endif
  82. _dmasettings_adc[0].destinationBuffer((uint16_t *)_buffer1, _buffer1_count * 2); // 2*b_size is necessary for some reason
  83. _dmasettings_adc[0].replaceSettingsOnCompletion(_dmasettings_adc[1]); // go off and use second one...
  84. _dmasettings_adc[0].interruptAtCompletion(); //interruptAtHalf or interruptAtCompletion
  85. #ifdef ADC_DUAL_ADCS
  86. _dmasettings_adc[1].source((volatile uint16_t &)((adc_num == 1) ? SOURCE_ADC_1 : SOURCE_ADC_0));
  87. _dmasettings_adc[1].destinationBuffer((uint16_t *)_buffer2, _buffer2_count * 2); // 2*b_size is necessary for some reason
  88. _dmasettings_adc[1].replaceSettingsOnCompletion(_dmasettings_adc[0]); // Cycle back to the first one
  89. _dmasettings_adc[1].interruptAtCompletion(); //interruptAtHalf or interruptAtCompletion
  90. #endif
  91. _dmachannel_adc = _dmasettings_adc[0];
  92. _stop_on_completion = false;
  93. }
  94. else
  95. {
  96. // Only one buffer so lets just setup the dmachannel ...
  97. // Serial.printf("AnalogBufferDMA::init Single buffer %d\n", adc_num);
  98. #ifdef ADC_DUAL_ADCS
  99. _dmachannel_adc.source((volatile uint16_t &)((adc_num == 1) ? SOURCE_ADC_1 : SOURCE_ADC_0));
  100. #else
  101. _dmachannel_adc.source((volatile uint16_t &)(SOURCE_ADC_0));
  102. #endif
  103. _dmachannel_adc.destinationBuffer((uint16_t *)_buffer1, _buffer1_count * 2); // 2*b_size is necessary for some reason
  104. _dmachannel_adc.interruptAtCompletion(); //interruptAtHalf or interruptAtCompletion
  105. _dmachannel_adc.disableOnCompletion(); // we will disable on completion.
  106. _stop_on_completion = true;
  107. }
  108. if (adc_num == 1)
  109. {
  110. #ifdef ADC_DUAL_ADCS
  111. _activeObjectPerADC[1] = this;
  112. _dmachannel_adc.attachInterrupt(&adc_1_dmaISR);
  113. _dmachannel_adc.triggerAtHardwareEvent(DMAMUX_ADC_1); // start DMA channel when ADC finishes a conversion
  114. #endif
  115. }
  116. else
  117. {
  118. _activeObjectPerADC[0] = this;
  119. _dmachannel_adc.attachInterrupt(&adc_0_dmaISR);
  120. _dmachannel_adc.triggerAtHardwareEvent(DMAMUX_ADC_0); // start DMA channel when ADC finishes a conversion
  121. }
  122. //arm_dcache_flush((void*)dmaChannel, sizeof(dmaChannel));
  123. _dmachannel_adc.enable();
  124. adc->adc[adc_num]->continuousMode();
  125. adc->adc[adc_num]->enableDMA();
  126. #ifdef DEBUG_DUMP_DATA
  127. dumpDMA_TCD(&_dmachannel_adc);
  128. dumpDMA_TCD(&_dmasettings_adc[0]);
  129. dumpDMA_TCD(&_dmasettings_adc[1]);
  130. #if defined(__IMXRT1062__) // Teensy 4.0
  131. if (adc_num == 1)
  132. {
  133. Serial.printf("ADC2: HC0:%x HS:%x CFG:%x GC:%x GS:%x\n", ADC2_HC0, ADC2_HS, ADC2_CFG, ADC2_GC, ADC2_GS);
  134. }
  135. else
  136. {
  137. Serial.printf("ADC1: HC0:%x HS:%x CFG:%x GC:%x GS:%x\n", ADC1_HC0, ADC1_HS, ADC1_CFG, ADC1_GC, ADC1_GS);
  138. }
  139. #endif
  140. #endif
  141. #else
  142. // Kinetisl (TLC)
  143. // setup a DMA Channel.
  144. // Now lets see the different things that RingbufferDMA setup for us before
  145. _dmachannel_adc.source((volatile uint16_t &)(SOURCE_ADC_0));
  146. ;
  147. _dmachannel_adc.destinationBuffer((uint16_t *)_buffer1, _buffer1_count * 2); // 2*b_size is necessary for some reason
  148. _dmachannel_adc.disableOnCompletion(); // ISR will hae to restart with other buffer
  149. _dmachannel_adc.interruptAtCompletion(); //interruptAtHalf or interruptAtCompletion
  150. _activeObjectPerADC[0] = this;
  151. _dmachannel_adc.attachInterrupt(&adc_0_dmaISR);
  152. _dmachannel_adc.triggerAtHardwareEvent(DMAMUX_ADC_0); // start DMA channel when ADC finishes a conversion
  153. _dmachannel_adc.enable();
  154. adc->startContinuous(adc_num);
  155. adc->enableDMA(adc_num);
  156. #ifdef DEBUG_DUMP_DATA
  157. dumpDMA_TCD(&_dmachannel_adc);
  158. #endif
  159. #endif
  160. _last_isr_time = millis();
  161. }
  162. //=============================================================================
  163. // stopOnCompletion: allows you to turn on or off stopping when a DMA buffer
  164. // has completed filling. Default is on when only one buffer passed in to the
  165. // constructor and off if two buffers passed in.
  166. //=============================================================================
  167. void AnalogBufferDMA::stopOnCompletion(bool stop_on_complete)
  168. {
  169. #ifndef KINETISL
  170. if (stop_on_complete)
  171. _dmachannel_adc.TCD->CSR |= DMA_TCD_CSR_DREQ;
  172. else
  173. _dmachannel_adc.TCD->CSR &= ~DMA_TCD_CSR_DREQ;
  174. #else
  175. if (stop_on_complete)
  176. _dmachannel_adc.CFG->DCR |= DMA_DCR_D_REQ;
  177. else
  178. _dmachannel_adc.CFG->DCR &= ~DMA_DCR_D_REQ;
  179. #endif
  180. _stop_on_completion = stop_on_complete;
  181. }
  182. //=============================================================================
  183. // ClearCompletion: if we have stop on completion, then clear the completion state
  184. // i.e. reenable the dma operation. Note only valid if we are
  185. // in the stopOnCompletion state.
  186. //=============================================================================
  187. bool AnalogBufferDMA::clearCompletion()
  188. {
  189. if (!_stop_on_completion)
  190. return false;
  191. // should probably check to see if we are dsiable or not...
  192. _dmachannel_adc.enable();
  193. return true;
  194. }
  195. //=============================================================================
  196. // processADC_DMAISR: Process the DMA completion ISR
  197. // common for both ISRs on those processors who have more than one ADC
  198. //=============================================================================
  199. void AnalogBufferDMA::processADC_DMAISR()
  200. {
  201. //digitalWriteFast(LED_BUILTIN, !digitalReadFast(LED_BUILTIN));
  202. uint32_t cur_time = millis();
  203. _interrupt_count++;
  204. _interrupt_delta_time = cur_time - _last_isr_time;
  205. _last_isr_time = cur_time;
  206. // update the internal buffer positions
  207. _dmachannel_adc.clearInterrupt();
  208. #ifdef KINETISL
  209. // Lets try to clear the previous interrupt, change buffers
  210. // and restart
  211. if (_buffer2 && (_interrupt_count & 1))
  212. {
  213. _dmachannel_adc.destinationBuffer((uint16_t *)_buffer2, _buffer2_count * 2); // 2*b_size is necessary for some reason
  214. }
  215. else
  216. {
  217. _dmachannel_adc.destinationBuffer((uint16_t *)_buffer1, _buffer1_count * 2); // 2*b_size is necessary for some reason
  218. }
  219. // If we are not stopping on completion, then reenable...
  220. if (!_stop_on_completion)
  221. _dmachannel_adc.enable();
  222. #endif
  223. }
  224. //=============================================================================
  225. // adc_0_dmaISR: called for first ADC when DMA has completed filling a buffer.
  226. //=============================================================================
  227. void AnalogBufferDMA::adc_0_dmaISR()
  228. {
  229. if (_activeObjectPerADC[0])
  230. {
  231. _activeObjectPerADC[0]->processADC_DMAISR();
  232. }
  233. #if defined(__IMXRT1062__) // Teensy 4.0
  234. asm("DSB");
  235. #endif
  236. }
  237. //=============================================================================
  238. // adc_1_dmaISR - Used for processors that have a second ADC object
  239. //=============================================================================
  240. void AnalogBufferDMA::adc_1_dmaISR()
  241. {
  242. if (_activeObjectPerADC[1])
  243. {
  244. _activeObjectPerADC[1]->processADC_DMAISR();
  245. }
  246. #if defined(__IMXRT1062__) // Teensy 4.0
  247. asm("DSB");
  248. #endif
  249. }
  250. #endif // ADC_USE_DMA