Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

External ADC Trigger for Teensy 3.x #67

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
13 changes: 13 additions & 0 deletions ADC.h
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down
149 changes: 147 additions & 2 deletions ADC_Module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -1719,4 +1725,143 @@ uint32_t ADC_Module::getQuadTimerFrequency()
}

#endif // Teensy 4
#endif // ADC_USE_QUAD_TIMER
#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
26 changes: 26 additions & 0 deletions ADC_Module.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}
Loading