mjs513
Senior Member+
Thanks that's why. Like I said think I am going to punt now and see what he comes up withFYI - Your test sketch does not compile on his branch as there is no such define as TIMEOUT as an error...
Thanks that's why. Like I said think I am going to punt now and see what he comes up withFYI - Your test sketch does not compile on his branch as there is no such define as TIMEOUT as an error...
#if ADC_USE_PDB
//! Start PDB triggering the ADC at the frequency
/** Call startSingleRead or startSingleDifferential on the pin that you want to measure before calling this function.
* See the example adc_pdb.ino.
* \param freq is the frequency of the ADC conversion, it can't be lower that 1 Hz
*/
void startPDB(uint32_t freq);
//! Stop the PDB
void stopPDB();
//! Return the PDB's frequency
uint32_t getPDBFrequency();
#endif
void startTimedReads(uint32_t freq);
void stopTimedReads()
uint32 getTimedReadsFrequency();
@mjs513 - I agree that right now doing much on the ADCL_t4, may not be that productive for us... I will spend some time trying to see how to migrate some of our additions/examples over for him to see, and potentially integrate into library.
Example his code has current code has none of the timed reads capability for T4.
Currently he has two different timing read mechanisms...
T3.2 use PGA
T3.x use PDB
TLC None
T4 None
But I have examples now using QTimer... The question will be to him, on add it? Is it some other different set of methods specific to T4?
That is instead of specific functions like:
Could be more generic... like:Code:#if ADC_USE_PDB //! Start PDB triggering the ADC at the frequency /** Call startSingleRead or startSingleDifferential on the pin that you want to measure before calling this function. * See the example adc_pdb.ino. * \param freq is the frequency of the ADC conversion, it can't be lower that 1 Hz */ void startPDB(uint32_t freq); //! Stop the PDB void stopPDB(); //! Return the PDB's frequency uint32_t getPDBFrequency(); #endif
Code:void startTimedReads(uint32_t freq); void stopTimedReads() uint32 getTimedReadsFrequency();
// try to use some teensy core functions...
// mainly out of pwm.c
#ifdef USE_TIMED_READS
extern "C" {
extern void xbar_connect(unsigned int input, unsigned int output);
extern void quadtimer_init(IMXRT_TMR_t *p);
extern void quadtimerWrite(IMXRT_TMR_t *p, unsigned int submodule, uint16_t val);
extern void quadtimerFrequency(IMXRT_TMR_t *p, unsigned int submodule, float frequency);
}
#endif
void startTimer(uint8_t trigger, uint8_t timer, uint32_t freq)
{
switch (trigger)
{
case 0:
if(timer == 1){
xbar_connect(XBARA1_IN_PIT_TRIGGER0, XBARA1_OUT_ADC_ETC_TRIG00); // pit to adc_etc
} else {
xbar_connect(XBARA1_IN_QTIMER4_TIMER0, XBARA1_OUT_ADC_ETC_TRIG00); // pit to adc_etc
}
break;
case 1:
if(timer == 1){
xbar_connect(XBARA1_IN_PIT_TRIGGER0, XBARA1_OUT_ADC_ETC_TRIG01); // pit to adc_etc
} else {
xbar_connect(XBARA1_IN_QTIMER4_TIMER0, XBARA1_OUT_ADC_ETC_TRIG01); // pit to adc_etc
} break;
case 2:
if(timer == 1){
xbar_connect(XBARA1_IN_PIT_TRIGGER0, XBARA1_OUT_ADC_ETC_TRIG02); // pit to adc_etc
} else {
xbar_connect(XBARA1_IN_QTIMER4_TIMER0, XBARA1_OUT_ADC_ETC_TRIG02); // pit to adc_etc
} break;
case 3:
if(timer == 1){
xbar_connect(XBARA1_IN_PIT_TRIGGER0, XBARA1_OUT_ADC_ETC_TRIG03); // pit to adc_etc
} else {
xbar_connect(XBARA1_IN_QTIMER4_TIMER0, XBARA1_OUT_ADC_ETC_TRIG03); // pit to adc_etc
} break;
case 4:
if(timer == 1){
xbar_connect(XBARA1_IN_PIT_TRIGGER0, XBARA1_OUT_ADC_ETC_TRIG04); // pit to adc_etc
} else {
xbar_connect(XBARA1_IN_QTIMER4_TIMER0, XBARA1_OUT_ADC_ETC_TRIG04); // pit to adc_etc
} break;
case 5:
if(timer == 1){
xbar_connect(XBARA1_IN_PIT_TRIGGER0, XBARA1_OUT_ADC_ETC_TRIG05); // pit to adc_etc
} else {
xbar_connect(XBARA1_IN_QTIMER4_TIMER0, XBARA1_OUT_ADC_ETC_TRIG05); // pit to adc_etc
} break;
case 6:
if(timer == 1){
xbar_connect(XBARA1_IN_PIT_TRIGGER0, XBARA1_OUT_ADC_ETC_TRIG06); // pit to adc_etc
} else {
xbar_connect(XBARA1_IN_QTIMER4_TIMER0, XBARA1_OUT_ADC_ETC_TRIG06); // pit to adc_etc
} break;
case 7:
if(timer == 1){
xbar_connect(XBARA1_IN_PIT_TRIGGER0, XBARA1_OUT_ADC_ETC_TRIG07); // pit to adc_etc
} else {
xbar_connect(XBARA1_IN_QTIMER4_TIMER0, XBARA1_OUT_ADC_ETC_TRIG07); // pit to adc_etc
} break;
default:
break;
}
if(timer == 1){
CCM_CCGR1 |= CCM_CCGR1_PIT(CCM_CCGR_ON);
PIT_MCR = 0;
IMXRT_PIT_CHANNELS[0].LDVAL = freq;
IMXRT_PIT_CHANNELS[0].TCTRL = PIT_TCTRL_TEN;
} else {
Serial.println("Try to init QTimer"); Serial.flush();
quadtimer_init(&IMXRT_TMR4);
quadtimerFrequency(&IMXRT_TMR4, 0, (float) freq);
quadtimerWrite(&IMXRT_TMR4, 0, 5);
Serial.println("After Qtimer init"); Serial.flush();
}
}
///// ADC0 ////
adc->setAveraging(1); // set number of averages
adc->setResolution(8); // set bits of resolution
adc->setConversionSpeed(ADC_CONVERSION_SPEED::VERY_HIGH_SPEED); // change the conversion speed
adc->setSamplingSpeed(ADC_SAMPLING_SPEED::VERY_HIGH_SPEED); // change the sampling speed
adc->adc0->startSingleRead(readPin)
adc->adc0->startPDB(freq)
adc->adc0->startTimer(3000);
adc->adc1->startTimer(2000);
xbar_connect(XBARA1_IN_QTIMER4_TIMER0, XBARA1_OUT_ADC_ETC_TRIG10); // pit to adc_etc
xbar_connect(XBARA1_IN_QTIMER4_TIMER0, XBARA1_OUT_ADC_ETC_TRIG00); // pit to adc_etc
Don't think you can have multiple timers on a single ADC like ADC1 with 2 timers, or if its even possible with XBAR? On initialization of the ADC_ETC it takes the ADC channel and the trigger group and link and links the config together. You can select any trigger group you want of course. Doesn't have to go in order. If you want to play I am attaching my latest with those added functions I mentioned above. I did a quick test and it didn't work but maybe there is another way?Trigger: You played with this a lot more than I have. So I have no clue on how best to set this part up. That is I have not played with any of the restrictions... That is what happens if I set trigger 1 and Trigger 3, but no others? This may be fine..
Not sure how that would work with interrupts unless, if an interrupt has occurred you test on the interrupt flag and then do the returns. Here is what I have:then wondered for chained, it made sense to have a new function(s), that you would call instead of startSingleRead()
maybe something like: startChainedRead(pin1, pin2, pin3, pin4); all but pin1 default to -1 ?
Again I am just thinking off the top of my head.
void adcetc0_isr() {
digitalWriteFast(LED_BUILTIN, !digitalReadFast(LED_BUILTIN));
ADC_ETC_DONE0_1_IRQ |= 1 << trigger_adc; // clear
//val0 = ADC_ETC_TRIG0_RESULT_1_0 & 4095;
adcValues[0] = ADC_ETC_GetADCConversionValue( trigger_adc, 0U ); /* Get trigger0 chain0 result. */
adcValues[1] = ADC_ETC_GetADCConversionValue( trigger_adc, 1U ); /* Get trigger0 chain1 result. */
adcValues[2] = ADC_ETC_GetADCConversionValue( trigger_adc, 2U );
adcValues[3] = ADC_ETC_GetADCConversionValue( trigger_adc, 3U );
asm("dsb");
}
Yep you would need a timer for each ADC. That was why for the test just used PIT for one tmer and QTimer for the second timer in my connectTimer and startTimer functions. The timers actually get linked in the initialization of ADC_ETC.Then question also may be, assume Timer is a QTimer.
Now should we allow the user to do something like:
Code:
adc->adc0->startTimer(3000);
adc->adc1->startTimer(2000);
If so Would need to setup two different timers. I used QTIMER4_0 as no IO is exposed that uses this (So No PWM and no frequency stuff).
4_1 and 4_2 can be used with pins 6 and 9, so maybe avoid, 4_3 is not on an IO pin exposed, so might be good choice for other.
With my DMA version, I used one QTimer, but did setup to XBAR_connects
Code:
xbar_connect(XBARA1_IN_QTIMER4_TIMER0, XBARA1_OUT_ADC_ETC_TRIG10); // pit to adc_etc
xbar_connect(XBARA1_IN_QTIMER4_TIMER0, XBARA1_OUT_ADC_ETC_TRIG00); // pit to adc_etc
the TRIG10, is for ADC2(ADC_1)
int adc_0_pins[] = {14, 15, 16, 17}; //sets pins for chaining on adc0
int adc_1_pins[] = {14, 15}; //sets pins for chaining on adc1
uint8_t trigger_adc_0 = 0; //for ADC_0 uses trigger 0, but in actuality can be any trig 0-3
uint8_t trigger_adc_1 = 4; //for ADC_1 uses trigger 4, but in actuality can be any trig 4-7
uint8_t chain_len_0 = 4; //chain length for ADC_0 (basically number of pins in adc_0_pins array
uint8_t chain_len_1 = 2; //chain length for ADC_1
//timer 1 = pit0, 2 = qtimer
connectTimer(1, trigger_adc_0);
connectTimer(2, trigger_adc_1);
startTimer(1, 24 * 1000); // 1 khz 1000 ADCs/sec
//use QTIMER (timer2)
startTimer(2, 3000.0); // try at 20 hz for test...
adc0_etc_ctrl_init(chain_len_0, //chainLength_0, chainLength_1
adc_0_pins, //*adc_0_pinArray, *adc_1_pinArray
trigger_adc_0, //trigger_adc_0, trigger_adc_1
false); //bool DMA
adc1_etc_ctrl_init(chain_len_1, //chainLength_0, chainLength_1
adc_1_pins, //*adc_0_pinArray, *adc_1_pinArray
trigger_adc_1, //trigger_adc_0, trigger_adc_1
false); //bool DMA
// ADC_ETC_SetTriggerChainConfig(trigger#, channel Group#, pin1, pin2);
ADC_ETC_SetTriggerChainConfig(ADC_0, trigger_adc_0, 0, adc_0_pins[0], adc_0_pins[1]);
ADC_ETC_SetTriggerChainConfig(ADC_0, trigger_adc_0, 1, adc_0_pins[2], adc_0_pins[3]);
ADC_ETC_SetTriggerChainConfig(ADC_1, trigger_adc_1, 0, adc_1_pins[0], adc_1_pins[1]);
attachInterruptVector(IRQ_ADC_ETC0, adcetc0_isr);
void adcetc1_isr() {
digitalWriteFast(LED_BUILTIN, !digitalReadFast(LED_BUILTIN));
ADC_ETC_DONE0_1_IRQ |= 1 << trigger_adc_0; // clear
ADC_ETC_DONE0_1_IRQ |= 1 << trigger_adc_1; // clear
//val0 = ADC_ETC_TRIG0_RESULT_1_0 & 4095;
for(uint8_t i=0; i < chain_len_0; i++) {
adcValues_0[i] = ADC_ETC_GetADCConversionValue( trigger_adc_0, i ); /* Get trigger0 chain0 result. */
}
for(uint8_t i=0; i < chain_len_1; i++) {
adcValues_1[i] = ADC_ETC_GetADCConversionValue( trigger_adc_1, i );
}
asm("dsb");
}
#include <ADC.h>
….
ADC *adc = new ADC(); // adc object
….
adc->setAveraging(8); // set number of averages
adc->setResolution(12); // set bits of resolution
adc->adc1->setResolution(8);
#elif defined(ADC_TEENSY_4)
const uint8_t ADC::channel2sc1aADC1[]= { // new version, gives directly the sc1a number. 0x1F=31 deactivates the ADC.
7, 8, 12, 11, 6, 5, 15, 0, 13, 14, 31, 31, 3, 4, // 0-13, we treat them as A0-A13
7, 8, 12, 11, 6, 5, 15, 0, 13, 14, // 14-23 (A0-A9)
31, 31, 3, 4 // A10, A11, A12, A13
};
#endif
ADC() : // awkward initialization so there are no -Wreorder warnings
#if ADC_DIFF_PAIRS > 0
adc0_obj(0, channel2sc1aADC0, diff_table_ADC0, ADC0_START)
#if ADC_NUM_ADCS>1
, adc1_obj(1, channel2sc1aADC1, diff_table_ADC1, ADC1_START)
#endif
#else
[COLOR="#FF0000"]adc0_obj(0, channel2sc1aADC0, ADC0_START)[/COLOR]
#if ADC_NUM_ADCS>1
, adc1_obj(1, channel2sc1aADC1, ADC1_START)
#endif
#endif
{
//ctor
//digitalWriteFast(LED_BUILTIN, HIGH);
}
int ADC_Module::analogRead(uint8_t pin) {
//digitalWriteFast(LED_BUILTIN, HIGH);
// check whether the pin is correct
if(!checkPin(pin)) {
fail_flag |= ADC_ERROR::WRONG_PIN;
return ADC_ERROR_VALUE;
}
// translate pin number to SC1A number, that also contains MUX a or b info.
const uint8_t sc1a_pin = channel2sc1a[pin];
Begin setup
End setup
Enter a command such as: s 3000<cr> to start doing something
Start Timer with frequency 3000 Hz.
*** ADC and ADC_ETC ***
ADC1: HC0:90 HS:0 CFG:12440 GC:0 GS:2
ADC2: HC0:90 HS:0 CFG:12440 GC:0 GS:2
ADC_ETC: CTRL:20000011 DONE0_1:0 DONE2_ERR:0 DMA: 0
TRIG[0] CTRL: 0 CHAIN_1_0:2017
TRIG[1] CTRL: 0 CHAIN_1_0:0
TRIG[2] CTRL: 0 CHAIN_1_0:0
TRIG[3] CTRL: 0 CHAIN_1_0:0
TRIG[4] CTRL: 0 CHAIN_1_0:2018
TRIG[5] CTRL: 0 CHAIN_1_0:0
TRIG[6] CTRL: 0 CHAIN_1_0:0
TRIG[7] CTRL: 0 CHAIN_1_0:0
Starting Timed read
ADC:0 delta time:166 freq:3012 - min:67 max:189 avg:128 rms:42
ADC:1 delta time:166 freq:3012 - min:84 max:172 avg:128 rms:30
Hello,Added in the startContinuous functions, need to test...
Hello,
I have converted a Teensy 3.2 to a Teensy 4.0 project. The showstopper in my project is startContinuous, and it seems to not be really implemented in the PJRC download of the Teensyduino library.
I'd be happy to beta-test startContinuous, if you want to collaborate by PM -- and compare its performance/compatibility with my Teensy 3.2 (in swap back-and-fourth comparisions).