diff --git a/ADC.h b/ADC.h index a41681b..20e50fa 100644 --- a/ADC.h +++ b/ADC.h @@ -180,6 +180,19 @@ extern "C" */ __attribute__((error("Use adc->adcX->setAveraging instead"))) void setAveraging(uint8_t num, int8_t adc_num = -1); + //! Enable Hardwaretrigger + /** + * This enables analog to digital conversion by PDB or LBTR triggering + */ + void setHardwareTrigger(int8_t adc_num = -1); + + //! Enable SoftwareTrigger + /** + * Enables analog to digital conversion by writing to SCA1 register + */ + void setSoftwareTrigger(int8_t adc_num = -1); + + //! Enable interrupts /** An IRQ_ADCx Interrupt will be raised when the conversion is completed * (including hardware averages and if the comparison (if any) is true). diff --git a/ADC_Module.cpp b/ADC_Module.cpp index 3b4e6ae..56dd58c 100644 --- a/ADC_Module.cpp +++ b/ADC_Module.cpp @@ -1591,10 +1591,16 @@ void ADC_Module::stopPDB() setSoftwareTrigger(); return; } + + // if (enablePDBisr) { NVIC_DISABLE_IRQ(IRQ_PDB); } + + // https://forum.pjrc.com/threads/25532-ADC-library-update-now-with-support-for-Teensy-3-1/ + // KurtE on 04-05-2018 12:06 PM + PDB0_CHnC1 = 0; // diasble ADC0 pre triggers + PDB0_SC = 0; setSoftwareTrigger(); - //NVIC_DISABLE_IRQ(IRQ_PDB); } //! Return the PDB's frequency @@ -1719,4 +1725,143 @@ uint32_t ADC_Module::getQuadTimerFrequency() } #endif // Teensy 4 -#endif // ADC_USE_QUAD_TIMER \ No newline at end of file +#endif // ADC_USE_QUAD_TIMER + + +//////////// EXT TRIG LPTMR //////////////// +//// External Trigger through LPTMR hardware block +//// https://github.com/manitou48/teensy3/blob/master/adclptmr.ino +// +// ADC trigger is on pin 13 +// +// Example ISRs +// ------------ +//volatile uint32_t aticks, lticks; +//void adc0_isr() { +// aticks++; +// adcval = ADC0_RA; // read and clear +//} +// +//void lptmr_isr(void) // not used +//{ +// LPTMR0_CSR |= LPTMR_CSR_TCF; // clear +// lticks++; +//} +// UU 2018-2020 + +#ifdef ADC_USE_LPTMR +void ADC_Module::startExtTrigLPTMR(bool enableLPTMRisr) { + + if (!(SIM_SCGC5 & SIM_SCGC5_LPTIMER)) { // setup LPTMR if not in use + SIM_SCGC5 |= SIM_SCGC5_LPTIMER; // enable LPTMR + CORE_PIN13_CONFIG = PORT_PCR_MUX(3); // pin 13 alt for LPTMR + } + + LPTMR0_CSR = 0; // Disable (necessary to make changes to LPTMR) + LPTMR0_PSR = LPTMR_PSR_PBYP; // Bypass prescaler/glitch filter + LPTMR0_CMR = 1; // Counts til interrupt occurs, rate = freq/(CMR+1) + // If CMR = 0 hardware trigger remains asserted until LPTMR disabled + if (enableLPTMRisr) { + LPTMR0_CSR = LPTMR_CSR_TIE | // Interrupt Enable + LPTMR_CSR_TPS(2) | // Pin: 0=CMP0, 1=xtal, 2=pin13 + //LPTMR_CSR_TFC | // Free-Running Counter + LPTMR_CSR_TMS; // Mode Select, 0=timer, 1=counter + } else { + LPTMR0_CSR = LPTMR_CSR_TPS(2) | // Pin: 0=CMP0, 1=xtal, 2=pin13 + //LPTMR_CSR_TFC | // Free-Running Counter + LPTMR_CSR_TMS; // Mode Select, 0=timer, 1=counter + } + LPTMR0_CSR |= LPTMR_CSR_TEN; // Enable + + // Set ADCn to trigger from the LPTMR + SIM_SOPT7 = (ADC_num? SIM_SOPT7_ADC1ALTTRGEN : SIM_SOPT7_ADC0ALTTRGEN) | // Enable ADCn alternate trigger + (ADC_num? SIM_SOPT7_ADC1TRGSEL(14) : SIM_SOPT7_ADC0TRGSEL(14)); // Trigger ADCn by LPTMR0 + setHardwareTrigger(); // trigger ADC with hardware + + if (enableLPTMRisr) { NVIC_ENABLE_IRQ(IRQ_LPTMR); } +} + +void ADC_Module::stopExtTrigLPTMR(bool enableLPTMRisr) { + + if (!(SIM_SCGC5 & SIM_SCGC5_LPTIMER)) { // if LPTMR wasn't enabled, return + return; + } + + if (enableLPTMRisr) { NVIC_DISABLE_IRQ(IRQ_LPTMR); } + + LPTMR0_CSR = 0; // Disable + setSoftwareTrigger(); +} + +#endif + +//////////// EXT TRIG PDB //////////////// +//// External Trigger though PDB hardware block +// https://forum.pjrc.com/threads/24492-Using-the-PDB-on-Teensy-3?p=128046&viewfull=1#post128046 +// manitou 12-20-2016 04:03 PM +// +// ADC trigger is on pin 11 +// +// Example ISRs +// ------------ +// +//volatile uint32_t pdbticks; +// +//void pdb_isr() { +// PDB0_SC = (PDB_SC_TRGSEL(00) | PDB_SC_PDBEN | PDB_SC_PDBIE ) | PDB_SC_LDOK; // (also clears interrupt flag) +// pdbticks++; +//} +// UU 2018-2020 + +#ifdef ADC_USE_PDB + +// frequency in Hz +void ADC_Module::startExtTrigPDB(bool enablePDBisr) { + if (!(SIM_SCGC6 & SIM_SCGC6_PDB)) { // Setup PDB + SIM_SCGC6 |= SIM_SCGC6_PDB; // Enable the PDB clock + CORE_PIN11_CONFIG = PORT_PCR_MUX(3); // pin 11 alt for PDB + } + + // remove later + // PDB pre trigger selection + //constexpr uint32_t PDB_CHnC1_TOS_1 = 0x0100; + //constexpr uint32_t PDB_CHnC1_EN_1 = 0x01; + //PDB0_CHnC1 = PDB_CHnC1_TOS_1 | PDB_CHnC1_EN_1; // enable pretrigger 0 (SC1A) + // remove later + + PDB0_IDLY = 0; // the pdb interrupt delay CNT+0 + // external trigger | PDB enable | interrupt enable + if (enablePDBisr) { + // might also need: continuous mode | load immediately + // | PDB_SC_CONT | PDB_SC_LDMOD(0); + PDB0_SC = (PDB_SC_TRGSEL(00) | PDB_SC_PDBEN | PDB_SC_PDBIE); + } else { + PDB0_SC = (PDB_SC_TRGSEL(00) | PDB_SC_PDBEN); + } + // loads the registers MOD, IDLY, CHnDLYm, DACINTx,and POyDLY after PDBEN is set + PDB0_SC |= PDB_SC_LDOK; + + // Set ADCn to trigger from the PDB + SIM_SOPT7 = (ADC_num? SIM_SOPT7_ADC1ALTTRGEN : SIM_SOPT7_ADC0ALTTRGEN) | // Enable ADCn alternate trigger + (ADC_num? SIM_SOPT7_ADC1TRGSEL(0) : SIM_SOPT7_ADC0TRGSEL(0)); // Trigger ADCn PDB0_EXTRG + // trigger ADC with hardware + setHardwareTrigger(); + + if (enablePDBisr) { NVIC_ENABLE_IRQ(IRQ_PDB); } +} + +void ADC_Module::stopExtTrigPDB(bool enablePDBisr) { + if (!(SIM_SCGC6 & SIM_SCGC6_PDB)) { // if PDB wasn't on, return + setSoftwareTrigger(); + return; + } + + if (enablePDBisr) {NVIC_DISABLE_IRQ(IRQ_PDB);} + + PDB0_CHnC1 = 0; // diasble ADC0 pre triggers + PDB0_SC = 0; // reset configuration register + + setSoftwareTrigger(); +} + +#endif \ No newline at end of file diff --git a/ADC_Module.h b/ADC_Module.h index 5ea245f..f9bb64c 100644 --- a/ADC_Module.h +++ b/ADC_Module.h @@ -586,6 +586,32 @@ class ADC_Module uint32_t getQuadTimerFrequency(); #endif + //////////// LPTMR Ext Trig//////////////// +#ifdef ADC_USE_LPTMR + //! Start external pin 13 LPTMR triggering the ADC + /** Call startSingleRead or startSingleDifferential on the pin that you want to measure after calling this function. + * See the example adc_lptmr.ino. + */ + void startExtTrigLPTMR(bool enableLPTMRisr); + void startExtTrigLPTMR() { startExtTrigLPTMR(false); } + //! Stop the LPTMR + void stopExtTrigLPTMR(bool enableLPTMRisr); + void stopExtTrigLPTMR() { stopExtTrigLPTMR(false); } +#endif + + //////////// PDB Ext Trig //////////////// +#ifdef ADC_USE_PDB + //! Start external pin 11 PDB triggering the ADC + /** Call startSingleRead or startSingleDifferential on the pin that you want to measure after calling this function. + * See the example adc_ext_pdb.ino. + */ + void startExtTrigPDB(bool enablePDBisr); + void startExtTrigPDB() { startExtTrigPDB(false); } + //! Stop the LPTMR + void stopExtTrigPDB(bool enablePDBisr); + void stopExtTrigPDB() { stopExtTrigPDB(false); } +#endif + //////// OTHER STUFF /////////// //! Store the config of the adc diff --git a/README.md b/README.md index 6b53b12..050133b 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,10 @@ If you download the library from here, also install the latest Teensyduino versi This thread in the Teensy forum also has information: http://forum.pjrc.com/threads/25532-ADC-library-update-now-with-support-for-Teensy-3-1 +# Updates + +This library was modified to include external trigger support (does not support Teensy 4.x) + # License Teensy 4.x, 3.x, LC ADC library diff --git a/examples/adc_external_clock/adc_lptr_extTrigger/adc_lptr_extTrigger.ino b/examples/adc_external_clock/adc_lptr_extTrigger/adc_lptr_extTrigger.ino new file mode 100644 index 0000000..6a2aa50 --- /dev/null +++ b/examples/adc_external_clock/adc_lptr_extTrigger/adc_lptr_extTrigger.ino @@ -0,0 +1,91 @@ +// Teensy external ADC clock example +// +// This program uses pin23 as "external" clock for ADC conversion. +// It increases the clock and reports if adc conversion were missed. +// You will need to create a connection between pin 23 and pin 13. +// +// We are using the LPTMR hardware module: +// adclptmr uses LPTMR0 counter (pin 13) to clock ADC A0 + +// ------------------------------------ +// Teensy 3.2 +// ------------------------------------ +//ADCr rates +// Averaging 0 ------------------------ +// 16 bit 735kS/s, VERY_HIGH, VERY_HIGH +// 12 bit 835kS/s, VERY_HIGH, VERY_HIGH +// 8 bit 975kS/s, VERY_HIGH, VERY_HIGH +// 16 bit 365kS/s, HIGH16, HIGH +// 16 bit 365kS/s, HIGH, HIGH +// 12 bit 430kS/s, HIGH, HIGH +// 8 bit 480kS/s, HIGH, HIGH +// Averaging 4 ------------------------ +// 16 bit 205kS/s, VERY_HIGH, VERY_HIGH +// 12 bit 255kS/s, VERY_HIGH, VERY_HIGH +// 8 bit 290kS/s, VERY_HIGH, VERY_HIGH +// ------------------------------------ + +#include "ADC.h" +ADC *adc = new ADC(); + +#define PIN_ADC 5 // A0, Pin 14 +#define PRINTREGISTER(x) Serial.print(#x" 0x"); Serial.println(x,HEX) + +volatile uint32_t lptmrticks, adcticks, adcval; + +void lptmr_isr(void) +{ + LPTMR0_CSR |= LPTMR_CSR_TCF; // clear + lptmrticks++; +} + +void adc0_isr() { + adcticks++; + adcval = adc->adc0->readSingle(); +} + +void adc_init() { + adc->adc0->disablePGA(); + adc->adc0->setReference(ADC_REFERENCE::REF_3V3); + adc->adc0->setAveraging(0); + adc->adc0->setResolution(8); + adc->adc0->disableCompare(); + adc->adc0->setConversionSpeed(ADC_CONVERSION_SPEED::VERY_HIGH_SPEED); + adc->adc0->setSamplingSpeed(ADC_SAMPLING_SPEED::VERY_HIGH_SPEED); +} + +void setup() { + Serial.begin(9600); + while (!Serial); + pinMode(PIN_ADC, INPUT); + pinMode(23, OUTPUT); + delay(1000); + adc_init(); +} + +float delta, expected; +uint32_t t, cnta=0, cntl=0; +elapsedMicros sinceStart_micros; + +void loop() { + for (long Fadc = 500000; Fadc < 2000000; Fadc = Fadc+5000) { + adc->adc0->startExtTrigLPTMR(true); // enable external LPTMR trigger and its interrupt + adc->adc0->startSingleRead(PIN_ADC); // call this to setup everything before the pdb starts, differential is also possible + adc->adc0->enableInterrupts(adc0_isr); + analogWriteResolution(4); // Setup trigger clock + analogWriteFrequency(23,long(Fadc*2)); // Create clock on pin13 + analogWrite(23, 8); // 50% dutycycle clock + sinceStart_micros = 0; + adcticks = 0; + lptmrticks = 0; + delay(1000); + t=sinceStart_micros, cnta=adcticks; cntl= lptmrticks; + adc->adc0->stopExtTrigLPTMR(true); // stop external LPTMR trigger and its interrupt + expected = t*1.e-6*Fadc; + delta = (cnta-expected)*1000000.0/t; + Serial.printf("Freq: %d", Fadc); + Serial.printf(" ADC %d", cnta); + Serial.printf(" LPT %d", cntl); + Serial.printf(" off by %+.3f Hz\n", delta); + } +} diff --git a/examples/adc_external_clock/adc_lptr_extTrigger_dma/adc_lptr_extTrigger_dma.ino b/examples/adc_external_clock/adc_lptr_extTrigger_dma/adc_lptr_extTrigger_dma.ino new file mode 100644 index 0000000..bcee682 --- /dev/null +++ b/examples/adc_external_clock/adc_lptr_extTrigger_dma/adc_lptr_extTrigger_dma.ino @@ -0,0 +1,130 @@ +// Teensy external tiggered ADC with DMA +// +// This program uses pin23 as "external" clock for ADC conversion. +// It increases the clock and reports if adc conversion were missed. +// You will need to create a connection between pin 23 and pin 13. +// +// We are using the LPTMR hardware module: +// adclptmr uses LPTMR0 counter (pin 13) to clock ADC A0 + + +// ------------------------------------ +// Teensy 3.2 +// ------------------------------------ +// ADC rates +// Averaging 0 ------------------------ +// 16 bit 735kS/s, VERY_HIGH, VERY_HIGH +// 12 bit 840kS/s, VERY_HIGH, VERY_HIGH +// 8 bit 940kS/s, VERY_HIGH, VERY_HIGH +// 16 bit 365kS/s, HIGH, HIGH +// 12 bit 430kS/s, HIGH, HIGH +// 8 bit 480kS/s, HIGH, HIGH +// Averaging 4 ------------------------ +// 16 bit 205kS/s, VERY_HIGH, VERY_HIGH +// 12 bit 255kS/s, VERY_HIGH, VERY_HIGH +// 8 bit 290kS/s, VERY_HIGH, VERY_HIGH +// ------------------------------------ + +#define BUFFERSIZE 16384 // The buffer takes dynamic memory. Stay below 85% of total dynamic memory +#include "ADC.h" +#include +ADC *adc = new ADC(); +DMAChannel dma0; +DMAMEM static uint16_t buf[BUFFERSIZE]; // buffer + +#define PIN_ADC 5 // ADC input pin +#define PRINTREGISTER(x) Serial.print(#x" 0x"); Serial.println(x,HEX) + +// variables used in the interrupt service routines (need to be volatile) +volatile uint32_t lptmrticks, adcticks, adcval; +volatile bool adc0_busy = false; + +// In case LPTMR interrrupt is turned on in ADC library +// Its not needed +void lptmr_isr(void) +{ + LPTMR0_CSR |= LPTMR_CSR_TCF; // clear + lptmrticks++; +} + +// ADC interrupt service routine +// Its not needed when DMA is enabled +void adc0_isr() { + adcticks++; + adcval = adc->adc0->readSingle(); // read and clear +} + +// DMA interrupt service routine +// This is called when the DMA buffer is full +void dma0_isr(void) { + adc0_busy = false; + dma0.clearInterrupt(); + dma0.clearComplete(); +} + +void adc_init() { + adc->adc0->disablePGA(); + adc->adc0->setReference(ADC_REFERENCE::REF_3V3); + adc->adc0->setAveraging(0); + adc->adc0->setResolution(8); + adc->adc0->disableCompare(); + adc->adc0->setConversionSpeed(ADC_CONVERSION_SPEED::VERY_HIGH_SPEED); + adc->adc0->setSamplingSpeed(ADC_SAMPLING_SPEED::VERY_HIGH_SPEED); + + // Initialize dma + dma0.source((volatile uint16_t&)ADC0_RA); + dma0.destinationBuffer(buf, sizeof(buf)); + dma0.triggerAtHardwareEvent(DMAMUX_SOURCE_ADC0); + dma0.interruptAtCompletion(); + //dma0.disableOnCompletion(); // Depends on what you want to do after DMA buffer is full + dma0.attachInterrupt(&dma0_isr); +} + +void setup() { + Serial.begin(9600); + while (!Serial); + pinMode(PIN_ADC, INPUT); + delay(1000); +} + +float delta, expected; +uint32_t t, cnta=0, cntl=0; +elapsedMicros sinceStart_micros; + +void loop() { + adc_init(); + memset((void*)buf, 0, sizeof(buf)); + for (long Fadc = 130000; Fadc < 2000000; Fadc = Fadc+5000) { + Serial.printf("Freq: %d", Fadc); + adc0_busy = true; + adc->adc0->startExtTrigLPTMR(true); // enable external LPTMR trigger and its interrupt + adc->adc0->startSingleRead(PIN_ADC); // call this to setup everything before the lptmr starts, differential is also possible + adc->adc0->enableInterrupts(adc0_isr); + adc->adc0->enableDMA(); // set ADC_SC2_DMAEN + dma0.enable(); // + // Set External clock for ADC + // connect pin 13 to pin 23 for this to work + analogWriteResolution(4); + analogWriteFrequency(23,long(Fadc*2)); // Create clock on pin13 + analogWrite(23, 8); // 50% dutycycle clock + sinceStart_micros = 0; + adcticks = 0; + lptmrticks = 0; + while (adc0_busy) { + // Serial.printf("%d %d\n", adcticks, lptmrticks); + } + t = sinceStart_micros; + cnta = adcticks; + cntl = lptmrticks; + // Stop ADC + dma0.disable(); + adc->adc0->disableDMA(); + adc->adc0->stopExtTrigLPTMR(true); + expected = t*1.e-6*Fadc; + delta = Fadc - (BUFFERSIZE / (t*1.e-6)); + Serial.printf(" ADC %d", cnta); + Serial.printf(" LPT %d", cntl); + Serial.printf(" off by %+.3f Hz\n", delta); + delay(100); + } +} diff --git a/examples/adc_external_clock/adc_pdb_extTrigger/adc_pdb_extTrigger.ino b/examples/adc_external_clock/adc_pdb_extTrigger/adc_pdb_extTrigger.ino new file mode 100644 index 0000000..779d40c --- /dev/null +++ b/examples/adc_external_clock/adc_pdb_extTrigger/adc_pdb_extTrigger.ino @@ -0,0 +1,95 @@ +// Teensy external ADC clock example +// +// This program uses pin23 as "external" clock for ADC conversion. +// It increases the clock and reports if adc conversion were missed. +// Connect pin 23 to pin 11. +// +// We are using PDB counter (pin 11) to clock ADC A0 + +// ------------------------------------ +// Teensy 3.2 +// ------------------------------------ +// ADC rates: +// Averaging 0 ------------------------ +// 16 bit 710kS/s, VERY_HIGH, VERY_HIGH +// 12 bit 830kS/s, VERY_HIGH, VERY_HIGH +// 8 bit 930kS/s, VERY_HIGH, VERY_HIGH +// 16 bit 365kS/s, HIGH, HIGH +// 12 bit 430kS/s, HIGH, HIGH +// 8 bit 480kS/s, HIGH, HIGH +// Averaging 4 ------------------------ +// 16 bit 205kS/s, VERY_HIGH, VERY_HIGH +// 12 bit 250kS/s, VERY_HIGH, VERY_HIGH +// 8 bit 290kS/s, VERY_HIGH, VERY_HIGH +// ------------------------------------ + +#include "ADC.h" +ADC *adc = new ADC(); + +#define PIN_ADC 5 // ADC input pin +#define PRINTREGISTER(x) Serial.print(#x" 0x"); Serial.println(x,HEX) + +// variables used in the interrupt service routines (need to be volatile) +volatile uint32_t pdbticks, adcticks, adcval; + +// Programmable Delay Block Interrupt Service Routine +void pdb_isr(void) +{ + PDB0_SC = (PDB_SC_TRGSEL(00) | PDB_SC_PDBEN | PDB_SC_PDBIE ) | PDB_SC_LDOK; // (also clears interrupt flag) + pdbticks++; +} + +// ADC interrupt service routine +// Its not needed when DMA is enabled +void adc0_isr() { + adcticks++; + adcval = adc->adc0->readSingle(); // read and clear +} + +void adc_init() { + adc->adc0->disablePGA(); // no gain + adc->adc0->setReference(ADC_REFERENCE::REF_3V3); // max reading is 3.3V + adc->adc0->setAveraging(0); // no averaging + adc->adc0->setResolution(12); // 12 bit adc + adc->adc0->disableCompare(); // no voltage range to compare input to + adc->adc0->setConversionSpeed(ADC_CONVERSION_SPEED::VERY_HIGH_SPEED); // Speed to convert to votlage to number + adc->adc0->setSamplingSpeed(ADC_SAMPLING_SPEED::VERY_HIGH_SPEED); // Time to sample signal, short time needs low impedance signal source +} + +void setup() { + while (!Serial && millis() < 3000) ; + Serial.begin(Serial.baud()); + Serial.println("ADC external trigger through PDB"); + + pinMode(PIN_ADC, INPUT); + delay(1000); + adc_init(); +} + +float delta, expected; +uint32_t t, cnta=0, cntp=0; +elapsedMicros sinceStart_micros; + +void loop() { + for (long Fadc = 200000; Fadc < 2000000; Fadc = Fadc+5000) { + Serial.printf("Freq: %d", Fadc); + adc->adc0->startExtTrigPDB(true); // enable external LPTMR trigger and its interrupt + adc->adc0->startSingleRead(PIN_ADC); // call this to setup everything before the lptmr starts, differential is also possible + adc->adc0->enableInterrupts(adc0_isr); + // Set External clock for ADC + // connect pin 11 to pin 23 for this to work + analogWriteResolution(4); // Prepare clock + analogWriteFrequency(23,long(Fadc)); // Create clock on pin13 + analogWrite(23, 8); // 50% dutycycle clock + sinceStart_micros = 0; adcticks = 0; pdbticks = 0; + delay(1000); + t = sinceStart_micros; cnta = adcticks; cntp = pdbticks; + // Stop ADC + adc->adc0->stopExtTrigPDB(true); + expected = t*1.e-6*Fadc; + delta = (cnta-expected )*1000000.0/t; + Serial.printf(" ADC %d", cnta); + Serial.printf(" PDB %d", cntp); + Serial.printf(" off by %+.3f Hz\n", delta); + } +} diff --git a/examples/adc_external_clock/adc_pdb_extTrigger_dma/adc_pdb_extTrigger_dma.ino b/examples/adc_external_clock/adc_pdb_extTrigger_dma/adc_pdb_extTrigger_dma.ino new file mode 100644 index 0000000..5961183 --- /dev/null +++ b/examples/adc_external_clock/adc_pdb_extTrigger_dma/adc_pdb_extTrigger_dma.ino @@ -0,0 +1,123 @@ +// Teensy external triggered ADC with DMA +// +// This program uses pin23 as "external" clock for ADC conversion. +// It increases the clock and reports if adc conversion were missed. +// Connect pin 23 to pin 11. +// +// We are using PDB counter (pin 11) to clock ADC A0 + +// ------------------------------------ +// Teensy 3.2 +// ------------------------------------ +// ADC rates: +// Averaging 0 ------------------------ +// 16 bit 710kS/s, VERY_HIGH, VERY_HIGH +// 12 bit 830kS/s, VERY_HIGH, VERY_HIGH +// 8 bit 930kS/s, VERY_HIGH, VERY_HIGH +// ------------------------------------ + +#define BUFFERSIZE 16384 // The buffer takes dynamic memory. Stay below 85% of total dynamic memory +#include "ADC.h" +#include +ADC *adc = new ADC(); +DMAChannel dma0; +DMAMEM static uint16_t buf[BUFFERSIZE]; // buffer + +#define PIN_ADC 5 // ADC input pin +#define PRINTREGISTER(x) Serial.print(#x" 0x"); Serial.println(x,HEX) + +// variables used in the interrupt service routines (need to be volatile) +volatile uint32_t pdbticks, adcticks, adcval; +volatile bool adc0_busy = false; + +// Programmable Delay Block Interrupt Service Routine +void pdb_isr(void) +{ + PDB0_SC = (PDB_SC_TRGSEL(00) | PDB_SC_PDBEN | PDB_SC_PDBIE ) | PDB_SC_LDOK; // (also clears interrupt flag) + pdbticks++; +} + +// ADC interrupt service routine +// Its not needed when DMA is enabled +void adc0_isr() { + adcticks++; + adcval = adc->adc0->readSingle(); // read and clear +} + +// DMA interrupt service routine +// This is called when the DMA buffer is full +void dma0_isr(void) { + adc0_busy = false; + dma0.clearInterrupt(); + dma0.clearComplete(); +} + +void adc_init() { + adc->adc0->disablePGA(); + adc->adc0->setReference(ADC_REFERENCE::REF_3V3); + adc->adc0->setAveraging(0); + adc->adc0->setResolution(16); + adc->adc0->disableCompare(); + adc->adc0->setConversionSpeed(ADC_CONVERSION_SPEED::VERY_HIGH_SPEED); + adc->adc0->setSamplingSpeed(ADC_SAMPLING_SPEED::VERY_HIGH_SPEED); + + // Initialize dma + dma0.source((volatile uint16_t&)ADC0_RA); + dma0.destinationBuffer(buf, sizeof(buf)); + dma0.triggerAtHardwareEvent(DMAMUX_SOURCE_ADC0); + dma0.interruptAtCompletion(); + //dma06.disableOnCompletion(); // Depends on what you want to do after DMA buffer is full + dma0.attachInterrupt(&dma0_isr); +} + +void setup() { + while (!Serial && millis() < 3000) ; + Serial.begin(Serial.baud()); + Serial.println("ADC external trigger through PDB with DMA"); + + pinMode(PIN_ADC, INPUT); + delay(1000); + adc_init(); +} + +float delta, expected; +uint32_t t, cnta=0, cntp=0; +elapsedMicros sinceStart_micros; + +void loop() { + memset((void*)buf, 0, sizeof(buf)); + for (long Fadc = 200000; Fadc < 2000000; Fadc = Fadc+5000) { + Serial.printf("Freq: %d", Fadc); + adc0_busy = true; + adc->adc0->startExtTrigPDB(true); // enable external LPTMR trigger and its interrupt + adc->adc0->startSingleRead(PIN_ADC); // call this to setup everything before the lptmr starts, differential is also possible + adc->adc0->enableInterrupts(adc0_isr); // not needed + adc->adc0->enableDMA(); // set ADC_SC2_DMAEN + dma0.enable(); // + // Set External clock for ADC + // connect pin 11 to pin 23 for this to work + analogWriteResolution(4); + analogWriteFrequency(23,long(Fadc)); // Create clock on pin13 + analogWrite(23, 8); // 50% dutycycle clock + sinceStart_micros = 0; + adcticks = 0; + pdbticks = 0; + while (adc0_busy) { + // Wait until dma buffer is full and dma initiates interrupt + // Serial.printf("%d %d\n", adcticks, lptmrticks); + } + t = sinceStart_micros; + cnta = adcticks; + cntp = pdbticks; + // Stop ADC + dma0.disable(); + adc->adc0->disableDMA(); + adc->adc0->stopExtTrigPDB(true); + expected = t*1.e-6*Fadc; + delta = Fadc - (BUFFERSIZE / (t*1.e-6)); + Serial.printf(" ADC %d", cnta); + Serial.printf(" PDB %d", cntp); + Serial.printf(" off by %+.3f Hz\n", delta); + delay(100); + } +} diff --git a/settings_defines.h b/settings_defines.h index f3cffe6..2d222d2 100644 --- a/settings_defines.h +++ b/settings_defines.h @@ -107,7 +107,7 @@ namespace ADC_settings #endif // Use PDB? -#if defined(ADC_TEENSY_3_1) // Teensy 3.1 +#if defined(ADC_TEENSY_3_1) // Teensy 3.1 #define ADC_USE_PDB #elif defined(ADC_TEENSY_3_0) // Teensy 3.0 #define ADC_USE_PDB @@ -116,7 +116,20 @@ namespace ADC_settings #define ADC_USE_PDB #elif defined(ADC_TEENSY_3_6) // Teensy 3.6 #define ADC_USE_PDB -#elif defined(ADC_TEENSY_4) // Teensy 4, 4.1 +#elif defined(ADC_TEENSY_4) // Teensy 4, 4.1 +#endif + +// Use LPTMR? +#if defined(ADC_TEENSY_3_1) // Teensy 3.1 +#define ADC_USE_LPTMR +#elif defined(ADC_TEENSY_3_0) // Teensy 3.0 +#define ADC_USE_LPTMR +#elif defined(ADC_TEENSY_LC) // Teensy LC +#elif defined(ADC_TEENSY_3_5) // Teensy 3.5 +#define ADC_USE_LPTMR +#elif defined(ADC_TEENSY_3_6) // Teensy 3.6 +#define ADC_USE_LPTMR +#elif defined(ADC_TEENSY_4) // Teensy 4, 4.1 #endif // Use Quad Timer