Teensy 4.0 which pins for which ADC

Status
Not open for further replies.
@KurtE

Yep - Distraction #1 is a lot more fun and easier to understand what you have to do.

ADC: What I found when I made those changes the print out of the DMA would alternate printing between values for pin 14 and pin 15. How do I know? Started with both pins on connected to 3.3v and then moved pin 15 input to ground so I would get 4093 then 0.

Need to init the ADC_1 object: does this say to use ADC2 instead of ADC1? Kind of defeats the point of chaining pins doesn't it? I don't know this is getting beyond me and giving me a headache.

If you have 2 pins on Chain1_0 the chain length is 0, think if you go and use chain2_3 (now 4 pins on 1 trigger, the chain length would increase to 2? Not sure about this one. Like I said out of element here and just experimenting.
 
@mjs513

Yes those distractions to this distraction are more up my comfort level and also things I play with more... But sometime fun to teach old dogs new tricks :D

Yes, lots of possibilities, all of which have some interesting capabilities, not sure how many of them we need/should understand/make work or examples of...

Actually both things we described would make sense to use (possibly at same time). Example on T3.6 with my probably never to complete well monitor code:
I have 4 Analogs coming in, which I do DMA from: Two on each ADC. Currently on an Interval Timer, which fires up a DMA operation used PDB on both channels, which do one set of the pins, on the next interval timer I update to do the other two pins... Currently at 3000 samples per second. With this setup, I could possibly chain both of the pins of each ADC to each other, run twice as many samples... And not have to fully alternate...

I am going to try a couple of experiments, to see what happens... For this I may undo using the DMA wrapper class I have, and put all of the code of it local, such that I might try for example in your updated version instead of two DMA DMA Settings linked in one DMAChannel, what if I used two DMAChannels? ...

Not sure yet how far I will get before distraction... Like test to see if the ili9341_t3n update still works on SPI1, SPI2...
 
@KurtE
Yeah - probably 2 analogs on each ADC is easier to implement with DMA to be honest. Just don't know how far we want to take this with the library format.

Not sure yet how far I will get before distraction... Like test to see if the ili9341_t3n update still works on SPI1, SPI2...
For me at least, I can understand what we are doing with the ILI9341 better :) Easier to get my head around. Going to play a little more but who knows.
 
@mjs513 - Yep ...

Actually I think I will back up a little before trying to get two timed DMA operations...

To try to get one to work on ADC2(_1) (either pin 26 or 27)
And see what ramifications it has with ADC_ETC...

I keep pulling out what few hairs that I still have out trying to understand some of this stuff.

Example: on ADC_ETC.TRIG[0-3] ... Specific to ADC1? and Optionally to ADC2 if the SYNC_MODE flag is set in the TRIG[].CTRL register?
If SYNC mode is not set? does that imply [0-3] are ADC1 specific and [4-7] ADC2 specific?

And/Or do you specify ADC1 or ADC2 in the TRIG[n], maybe by the HWTS1(2) values where maybe this is a bitmask for which one(s) to trigger,
Where maybe the 4 low order bits specify ADC1(R0-R3) and top 4 ADC2 (R0-R3)?

The manual is totally clear as mud to me!

But maybe need more (or less) coffee!
 
@mjs513 - Yep ...

Actually I think I will back up a little before trying to get two timed DMA operations...

To try to get one to work on ADC2(_1) (either pin 26 or 27)
And see what ramifications it has with ADC_ETC...

I keep pulling out what few hairs that I still have out trying to understand some of this stuff.

Example: on ADC_ETC.TRIG[0-3] ... Specific to ADC1? and Optionally to ADC2 if the SYNC_MODE flag is set in the TRIG[].CTRL register?
If SYNC mode is not set? does that imply [0-3] are ADC1 specific and [4-7] ADC2 specific?

And/Or do you specify ADC1 or ADC2 in the TRIG[n], maybe by the HWTS1(2) values where maybe this is a bitmask for which one(s) to trigger,
Where maybe the 4 low order bits specify ADC1(R0-R3) and top 4 ADC2 (R0-R3)?

The manual is totally clear as mud to me!

But maybe need more (or less) coffee!

Was looking at that this morning for something else and yeah its clear as mud to me as well.

Don't if you drink more or less coffee it would help - maybe something stronger :)
 
@mjs513 - I pushed up my copy of the use QTimer ADC_ETC, ... That does the timed DMA on one pin...

First pass only worked on ADC1(ADC_0).

So I hacked on it to have an option to try to work on pin 26 on ADC2(ADC_1). Finally got a version that appears to work. (That version is now pushed up) :D

In case anyone is interested, some of the changes (other than more debug outputs and the obvious initialize the 2nd ADC)...

Changed ADC_ETC to use channel 4 instead of 0

Changed the XBAR settings from output of: XBARA1_OUT_ADC_ETC_TRIG00 to XBARA1_OUT_ADC_ETC_TRIG10

adc_init - change to update adc2 instead of adc1

in the adc_etc_init:
obviously change to us pin mapping for ADC2 instead of ADC1

in TRIG[4].CTRL - don't bypass change the ADC_ETC_CTRL_TRIG_ENABLE to (1<<4)
in TRIG[4].CHAIN_1_0 changed to the right pin channel
in DMA_CTRL change ADC_ETC_DMA_CTRL_TRIQ_ENABLE from 0 to 4

May take a break before I then try to get a version that works with both...
 
@KurtE

Went down a little bit separate path than you did. I took the interrupt version and put 4 pins on one ADC. I did steal a function from the SDK to get the conversion values that is essentially based on the pin number. Looks like it is working. Right now its set up to use pins A0-A3, with this method you could get up to 8 pins with one trigger on one adc.
Code:
#include <ADCL_t4.h>

#define USE_TIMED_READS
#define USE_PIT0


const int readPin = A0;
const int readPin2 = A1;
const int readPin3 = A2;
const int readPin4 = A3;

ADCL *adc = new ADCL(); // adc object
// chain A0 A1   AD_B1_02  AD_B1_03   ADC1 IN7 8
#define PRREG(x) Serial.print(#x" 0x"); Serial.println(x,HEX)
volatile uint32_t val0, val1;
volatile uint32_t adcValues[4];

void adcetc0_isr() {
  digitalWriteFast(LED_BUILTIN, !digitalReadFast(LED_BUILTIN));
  ADC_ETC_DONE0_1_IRQ |= 1;   // clear
  //val0 = ADC_ETC_TRIG0_RESULT_1_0 & 4095;
  adcValues[0] = ADC_ETC_GetADCConversionValue(0U, 0U ); /* Get trigger0 chain0 result. */
  adcValues[1] = ADC_ETC_GetADCConversionValue( 0U, 1U ); /* Get trigger0 chain1 result. */
  adcValues[2] = ADC_ETC_GetADCConversionValue( 0U, 2U );
  adcValues[3] = ADC_ETC_GetADCConversionValue( 0U, 3U );
  asm("dsb");
}

void adcetc1_isr() {
  //digitalWriteFast(LED_BUILTIN, !digitalReadFast(LED_BUILTIN));
  ADC_ETC_DONE0_1_IRQ |= 1;   // clear
  //val0 = ADC_ETC_TRIG0_RESULT_1_0 & 4095;
  adcValues[0] = ADC_ETC_GetADCConversionValue(0U, 0U ); /* Get trigger0 chain0 result. */
  adcValues[1] = ADC_ETC_GetADCConversionValue( 0U, 1U ); /* Get trigger0 chain1 result. */
  adcValues[2] = ADC_ETC_GetADCConversionValue( 0U, 2U );
  adcValues[3] = ADC_ETC_GetADCConversionValue( 0U, 3U );
  asm("dsb");
}

void setup() {
  while (!Serial && millis() < 5000) ;

  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(readPin, INPUT); //pin 23 single ended
  pinMode(readPin2, INPUT); //pin 23 single ended
  pinMode(readPin3, INPUT); //pin 23 single ended
  pinMode(readPin4, INPUT); //pin 23 single ended
  
  Serial.begin(9600);
  Serial.println("Setup ADC_0");
  // reference can be ADC_REFERENCE::REF_3V3, ADC_REFERENCE::REF_1V2 (not for Teensy LC) or ADC_REF_EXT.
  //adc->setReference(ADC_REFERENCE::REF_1V2, ADC_0); // change all 3.3 to 1.2 if you change the reference to 1V2

  adc->setAveraging(8); // set number of averages
  adc->setResolution(12); // set bits of resolution


  // always call the compare functions after changing the resolution!
  //adc->enableCompare(1.0/3.3*adc->getMaxValue(ADC_0), 0, ADC_0); // measurement will be ready if value < 1.0V
  //adc->enableCompareRange(1.0*adc->getMaxValue(ADC_1)/3.3, 2.0*adc->getMaxValue(ADC_1)/3.3, 0, 1, ADC_1); // ready if value lies out of [1.0,2.0] V

#ifdef USE_TIMED_READS
  setup_adc_hardware_trigger(readPin, readPin2);
#else
  adc->analogRead(readPin, ADC_0);
#endif
  Serial.println("End Setup");

}

void loop() {
  Serial.printf("%d  %d  %d  %d\n", adcValues[0], adcValues[1], adcValues[2], adcValues[3]);
  delay(500);
}

/*!
* Taken from SDK
* brief Get ADC conversion result from external XBAR sources.
* For example, if triggerGroup is set to 0U and chainGroup is set to 1U, which means the API would
* return Trigger0 source's chain1 conversion result.
*
* param base ADC_ETC peripheral base address.
* param triggerGroup Trigger group index. Available number is 0~7.
* param chainGroup Trigger chain group index. Available number is 0~7.
* return ADC conversion result value.
*/
uint32_t ADC_ETC_GetADCConversionValue(uint32_t triggerGroup, uint32_t chainGroup)
{
    //assert(triggerGroup < ADC_ETC_TRIGn_RESULT_1_0_COUNT);

    uint32_t mADCResult;
    uint8_t mRemainder = chainGroup % 2U;

    switch (chainGroup / 2U)
    {
        case 0U:
            if (0U == mRemainder)
            {
                mADCResult = (0xFFFU) & (IMXRT_ADC_ETC.TRIG[triggerGroup].RESULT_1_0);
            }
            else
            {
                mADCResult = (IMXRT_ADC_ETC.TRIG[triggerGroup].RESULT_1_0) >> (16U);
            }
            break;
        case 1U:
            if (0U == mRemainder)
            {
                mADCResult = (0xFFFU) & (IMXRT_ADC_ETC.TRIG[triggerGroup].RESULT_3_2);
            }
            else
            {
                mADCResult = (IMXRT_ADC_ETC.TRIG[triggerGroup].RESULT_3_2) >> (16U);
            }
            break;
        case 2U:
            if (0U == mRemainder)
            {
                mADCResult = (0xFFFU) & (IMXRT_ADC_ETC.TRIG[triggerGroup].RESULT_5_4);
            }
            else
            {
                mADCResult = (IMXRT_ADC_ETC.TRIG[triggerGroup].RESULT_5_4) >> (16U);
            }
            break;
        case 3U:
            if (0U == mRemainder)
            {
                mADCResult = (0xFFFU) & (IMXRT_ADC_ETC.TRIG[triggerGroup].RESULT_7_6);
            }
            else
            {
                mADCResult = (IMXRT_ADC_ETC.TRIG[triggerGroup].RESULT_7_6) >> (16U);
            }
            break;
        default:
            return 0U;
    }
    return mADCResult;
}

void setup_adc_hardware_trigger(uint8_t pin,uint8_t pin2)
{

  xbar_init();
  Serial.println("After XBAR"); Serial.flush();
  adc_init();
  // dma_init();  // hopefully already done.
  adc_etc_init(pin, pin2);
  Serial.println("After ADC/ADC_ETC"); Serial.flush();

#ifdef USE_PIT0
  pit_init(24 * 1000);   // 1 khz  1000 ADCs/sec
#else
  qtimer_init(3000.0);  // try at 20 hz for test...
#endif

  Serial.printf("ADC1: HC0:%x HS:%x CFG:%x GC:%x GS:%x\n", ADC1_HC0, ADC1_HS,  ADC1_CFG, ADC1_GC, ADC1_GS);
  Serial.printf("ADC_ETC: CTRL:%x DMA: %x TRIG0: CTRL: %x CHAIN01:%x\n",
                IMXRT_ADC_ETC.CTRL, IMXRT_ADC_ETC.DMA_CTRL, ADC_ETC_TRIG0_CTRL, ADC_ETC_TRIG0_CHAIN_1_0);
}

#ifdef USE_TIMED_READS
// try to use some teensy core functions...
// mainly out of pwm.c
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);
}

void xbar_init() {
  CCM_CCGR2 |= CCM_CCGR2_XBAR1(CCM_CCGR_ON);   //turn clock on for xbara1
#ifdef USE_PIT0
  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
#endif
}

void adc_init() {
  ADC1_CFG |= ADC_CFG_ADTRG;   // hardware trigger
  ADC1_HC0 = 16;   // ADC_ETC channel
  ADC1_HC1 = 16;   // ADC_ETC channel
  //ADC1_GC &= ~ADC_GC_ADCO;

  attachInterruptVector(IRQ_ADC_ETC0, adcetc0_isr);
  NVIC_ENABLE_IRQ(IRQ_ADC_ETC0);
  attachInterruptVector(IRQ_ADC_ETC1, adcetc0_isr);
  NVIC_ENABLE_IRQ(IRQ_ADC_ETC1);
}

void adc_etc_init(uint8_t pin, uint8_t pin2) {

  IMXRT_ADC_ETC.CTRL = ADC_ETC_CTRL_SOFTRST; // SOFTRST
  IMXRT_ADC_ETC.CTRL &= ~ADC_ETC_CTRL_SOFTRST; // SOFTRST
  delay(5);
  
  uint8_t adc_pin_channel = ADCL::mapPinToChannel(pin);
  Serial.print("ADC Pin1 Channel: "); Serial.println(adc_pin_channel);
  IMXRT_ADC_ETC.CTRL = (ADC_ETC_CTRL_TSC_BYPASS | ADC_ETC_CTRL_TRIG_ENABLE(1)); // 0x40000001;  // start with trigger 0
  Serial.printf("ADC_ETC_CTRL: %x %x\n",  IMXRT_ADC_ETC.CTRL, ADC_ETC_CTRL_TRIG_ENABLE(1));
  
  IMXRT_ADC_ETC.TRIG[0].CTRL = ADC_ETC_TRIG_CTRL_TRIG_CHAIN(3);   // chainlength -1 only us

  IMXRT_ADC_ETC.TRIG[0].CHAIN_1_0 = ADC_ETC_TRIG_CHAIN_B2B0 |
    ADC_ETC_TRIG_CHAIN_IE0(1) /*| ADC_ETC_TRIG_CHAIN_B2B0 */
    | ADC_ETC_TRIG_CHAIN_HWTS0(1) | ADC_ETC_TRIG_CHAIN_CSEL0(adc_pin_channel) ;

  adc_pin_channel = ADCL::mapPinToChannel(pin2);
  Serial.print("ADC Pin2 Channel: "); Serial.println(adc_pin_channel);
  IMXRT_ADC_ETC.TRIG[0].CHAIN_1_0 |= ADC_ETC_TRIG_CHAIN_B2B1 | 
    ADC_ETC_TRIG_CHAIN_IE1(2) /*| ADC_ETC_TRIG_CHAIN_B2B0 */
    | ADC_ETC_TRIG_CHAIN_HWTS1(2) | ADC_ETC_TRIG_CHAIN_CSEL1(adc_pin_channel); 

  Serial.print("TRIG[0].CHAIN_1_0: "); Serial.println(IMXRT_ADC_ETC.TRIG[0].CHAIN_1_0, HEX);

  //ADC_ETC_TRIG0_CHAIN_1_0 = 0x1017;   // ADC1 7  chain channel, HWTS,  BB? TODO
  // From other sample
  //ADC_ETC_TRIG0_CTRL = ADC_ETC_TRIG_CTRL_SYNC_MODE; // 0x100;   // chainlength -1
  //ADC_ETC_TRIG0_CHAIN_1_0 = 0x50283017;   // ADC1 7 8, chain channel, HWTS, IE, B2B

  uint32_t tmp, tmp32;
      adc_pin_channel = ADCL::mapPinToChannel(readPin3);
        Serial.print("ADC Pin3 Channel: "); Serial.println(adc_pin_channel);
  IMXRT_ADC_ETC.TRIG[0].CHAIN_3_2 = ADC_ETC_TRIG_CHAIN_B2B0 |
    ADC_ETC_TRIG_CHAIN_IE0(1) /*| ADC_ETC_TRIG_CHAIN_B2B0 */
    | ADC_ETC_TRIG_CHAIN_HWTS0(1) | ADC_ETC_TRIG_CHAIN_CSEL0(adc_pin_channel) ;
      
      adc_pin_channel = ADCL::mapPinToChannel(readPin4);
        Serial.print("ADC Pin4 Channel: "); Serial.println(adc_pin_channel);
  IMXRT_ADC_ETC.TRIG[0].CHAIN_3_2 |= ADC_ETC_TRIG_CHAIN_B2B1 | 
    ADC_ETC_TRIG_CHAIN_IE1(2) /*| ADC_ETC_TRIG_CHAIN_B2B0 */
    | ADC_ETC_TRIG_CHAIN_HWTS1(2) | ADC_ETC_TRIG_CHAIN_CSEL1(adc_pin_channel); 
  Serial.print("TRIG[0].CHAIN_3_2: "); Serial.println(IMXRT_ADC_ETC.TRIG[0].CHAIN_3_2, HEX);


  attachInterruptVector(IRQ_ADC_ETC0, adcetc0_isr);
  NVIC_ENABLE_IRQ(IRQ_ADC_ETC0);
  attachInterruptVector(IRQ_ADC_ETC1, adcetc0_isr);
  NVIC_ENABLE_IRQ(IRQ_ADC_ETC1);
}

#ifdef USE_PIT0
void pit_init(uint32_t cycles)
{
  CCM_CCGR1 |= CCM_CCGR1_PIT(CCM_CCGR_ON);
  PIT_MCR = 0;

  IMXRT_PIT_CHANNELS[0].LDVAL = cycles;
  IMXRT_PIT_CHANNELS[0].TCTRL = PIT_TCTRL_TEN;
}
#else
void  qtimer_init(float freq)  // try at 20 hz for test...
{
  Serial.println("Try to init QTimer"); Serial.flush();
  quadtimer_init(&IMXRT_TMR4);
  quadtimerFrequency(&IMXRT_TMR4, 0, freq);
  quadtimerWrite(&IMXRT_TMR4, 0, 5);

  Serial.println("After Qtimer init"); Serial.flush();

}
#endif

#endif
 
@mjs513 -

Looks cool, may have to combine functionality :D

Chaining reads could be interesting. Some actual plus from the T4 ADC module...

This morning having some fun, this morning got a sketch to I think work with doing Timed DMA reads using QTimer4.0 to two different DMA Channels, and my quick and dirty RMS(ish) code appears to show some looking reasonable data from the two sine waves I have being generated by T3.6 using the Audio sketch...

The interesting thing was, before I was hacking up the DMA Source to be DMAMUX_SOURCE_ADC_ETC which was working good with one DMA... But it was totally screwing up with two DMAs trying to use same logical source. So I removed the override and allowed it to use default ones: DMAMUX_SOURCE_ADC1 and DMAMUX_SOURCE_ADC2 and now it is working fine...

I am asking for 1600 samples at 3000 per second and the DMA interrupt at completion is happening after about 533ms.

If you are interested, I included the current sketch..

Maybe should add it plus your current one as examples!

Soon will try to
a) make an attempt to start merging some/all of ADCL_t4 into ADC library
b) Converting some of these sketches into extensions of the library...
 

Attachments

  • T4_ADC_DMA_QTIMER_RMS_BOTH-191223a.zip
    4.6 KB · Views: 90
This morning having some fun, this morning got a sketch to I think work with doing Timed DMA reads using QTimer4.0 to two different DMA Channels, and my quick and dirty RMS(ish) code appears to show some looking reasonable data from the two sine waves I have being generated by T3.6 using the Audio sketch...
So you have timed ADC reads with DMA? Is this not the key to supporting T4 ADC in audio lib? are you doing double-buffered DMA -- half-complete + complete ISR.
 
@KurtE
Added a function based on the function (basically just the name):
Code:
  //  ADC_ETC_SetTriggerChainConfig(trigger#, channel Group#, pin1, pin2);
  ADC_ETC_SetTriggerChainConfig(0, 0, readPin, readPin2);
  ADC_ETC_SetTriggerChainConfig(0, 1, readPin3, readPin4);

Would replace all the setup for the chain config with 1 line setup. Not sure what to do next :)
 

Attachments

  • ADCLpitxbaradcV3.zip
    3.1 KB · Views: 53
KurtE said:
If you are interested, I included the current sketch..

Maybe should add it plus your current one as examples!

Soon will try to
a) make an attempt to start merging some/all of ADCL_t4 into ADC library
b) Converting some of these sketches into extensions of the library...
Of course interested. Working on trying to simplify some of the setup will see what I want to do next :)
 
So you have timed ADC reads with DMA? Is this not the key to supporting T4 ADC in audio lib? are you doing double-buffered DMA -- half-complete + complete ISR.

Yep - I have timed reads (Qtimer4.0) adc reads with DMA... As for Analog library, I have not played much with the internals to know what it needs.

Right now it is sort of double buffered, needs better control...
I was having issues with their Ring buffer stuff, so instead, my setup function passes in two buffers, which I then have 2 DMASettings chained to each other round robin with a DMAChannel... And currently I have both DMASettings do an InterruptOnCompletion...

Again needs some cleanup, but making progress!

@mjs513... When I am not in other seasonal distractions, I am trying to make a first pass of integrating the rudimentary stuff into ADC library.
 
KurtE said:
@mjs513... When I am not in other seasonal distractions, I am trying to make a first pass of integrating the rudimentary stuff into ADC library.
Got it - will stop trying to make generic functions until your merge is complete :)
 
@mjs513 - Go for it...

This most likely be a iterative process. Right not just trying to get high level module code over. There are many things that were not defined...

Now in process of changing the functions for convert pin to channel...

But I don't think your adding new stuff will hinder this in any way...
 
@KurtE
Will do!

Was just looking at your latest DMA example and so far the 2 functions I generated will work with your DMA example at least the chaining setup. Interrupt stuff NA for DMA :)
 
@KurtE
Came up with a new function but think its too much in one - thinking about breaking it up to adc_0 and adc_1 init? Suggestions:
Code:
void adc_etc_ctrl_init(uint8_t chainLength_0, uint8_t chainLength_1,
                  uint8_t *adc_0_pinArray, uint8_t *adc_1_pinArray, 
                  uint8_t trigger_adc_0, uint8_t trigger_adc_1, bool DMA) 
{
  
  uint8_t numPins_adc_0 = sizeof(adc_0_pinArray);
  uint8_t numPins_adc_1 = sizeof(adc_1_pinArray);
  uint8_t adc_pin_channel;

  delay(5);

  // Now lets process ADC1(ADC_0) pin...
  if(numPins_adc_0 != 0){
  for(uint8_t i = 0; i < numPins_adc_0; i++){
    adc_pin_channel = ADCL::mapPinToChannel(adc_0_pinArray[i], ADC_0);
    if (adc_pin_channel == 0xff) 
      Serial.printf("ADC_ETC_INIT: pin:%d did not map to channnel on %d\n", adc_0_pinArray[i], ADC_0);
  }
  IMXRT_ADC_ETC.CTRL = (ADC_ETC_CTRL_DMA_MODE_SEL | ADC_ETC_CTRL_TRIG_ENABLE(1)); // 0x40000001;  // start with trigger 0
  Serial.printf("ADC_ETC_CTRL: %x %x\n",  IMXRT_ADC_ETC.CTRL, ADC_ETC_CTRL_TRIG_ENABLE(1));
  
  IMXRT_ADC_ETC.TRIG[trigger_adc_0].CTRL = ADC_ETC_TRIG_CTRL_TRIG_CHAIN(chainLength_0-1);

  if(DMA)
    IMXRT_ADC_ETC.DMA_CTRL = ADC_ETC_DMA_CTRL_TRIQ_ENABLE(trigger_adc_0);
  }
  
  // Now lets do ADC2(ADC_1)
  if(numPins_adc_1 != 0){
  for(uint8_t i = 0; i < numPins_adc_1; i++){
    adc_pin_channel = ADCL::mapPinToChannel(adc_1_pinArray[i], ADC_1);
    if (adc_pin_channel == 0xff) 
      Serial.printf("ADC_ETC_INIT: pin:%d did not map to channnel on %d\n", adc_1_pinArray[i], ADC_1);
  }
  // ADC2 (ADC_1)
  // Guess that 4 trigger is for ADC2?
  IMXRT_ADC_ETC.CTRL |= ADC_ETC_CTRL_TRIG_ENABLE(1 << trigger_adc_1); // Add in our trigger
  Serial.printf("ADC_ETC_CTRL: %x\n",  IMXRT_ADC_ETC.CTRL);
  
  IMXRT_ADC_ETC.TRIG[trigger_adc_1].CTRL = ADC_ETC_TRIG_CTRL_TRIG_CHAIN(chainLength_0-1);
  
  if(DMA)
    IMXRT_ADC_ETC.DMA_CTRL |= ADC_ETC_DMA_CTRL_TRIQ_ENABLE(trigger_adc_1);
  }
}
works like this:
Code:
void adc_etc_init() {

  adc_etc_reset();
 
  adc_etc_ctrl_init(4, 0, //chainLength_0, chainLength_1
                    adc_0_pinArray, adc_1_pinArray, //*adc_0_pinArray, *adc_1_pinArray
                    0, 0, //trigger_adc_0,trigger_adc_1
                    false); //bool DMA

  //  ADC_ETC_SetTriggerChainConfig(trigger#, channel Group#, pin1, pin2);
  ADC_ETC_SetTriggerChainConfig(0, 0, readPin, readPin2);
  ADC_ETC_SetTriggerChainConfig(0, 1, readPin3, readPin4);

  attachInterruptVector(IRQ_ADC_ETC0, adcetc0_isr);
  NVIC_ENABLE_IRQ(IRQ_ADC_ETC0);
}
thoughts

EDIT: not working right - something wrong with my pin arrays if I have a array empty :(
 
Last edited:
Looks like you are having some fun.

I think I have a partially working version of ADC library that at least I can compile for the readPin example and it appears to read on both pins...

I have a new branch(T4) part of my ADC fork, that I am testing stuff with now.

Your new stuff look interesting. Will have to take more of look soon.

Next up try testing a little more complex example...
 
Updated the T4 library added my DMA buffer class with some mods.
Like make it hopefully work for T3.x....

My Dual DMA test sketch (not tied to timer) works after a few edits to the sketch for T3.x...
Will double check OK for T4 (was before edit).

On T4, when I turned on DMA it also turned on continuous mode in the ADC, where as for T3.x, they don't. So will probably change T4 to match.

Since continuous update was not turned on, needed to change sketch that stated the reads from just calling
adc->readAnalog(pin, ADC_x)
to instead call
adc->startContinuous(readPin, ADC_0);

Then things appeared to be running!
 
@mjs513...

Just received Github email from @pedvide:
Hey @KurtE , checkout the "teensy4" branch... :)
It sort of works, but I need to check everything thoroughly.
I had a look at your code to speed up writing mine, so thanks a lot!
My plans are to make sure everything works and post it in the forum.
Then I want to make the library more efficient, especially for the older and smaller boards.

Also looks like he has checked in some other changes into the master branch as well.
 
@KurtE

Saw his comments looks like he's getting back into it.

OK - I downloaded your branch last night but lost on how to use it for the T4. I saw that you created a adc_module_t4 that has its own constructor which has all the stuff in it that use be the equivalent of ADCL.h.

Tried my compare example but was using the T3.x function instead of the T4 version which is in the t4 module
 
@mjs513 - Assuming I did not completely screw up the whole thing ;) which is possible,

The T3.x/TLC module code should be #ifdef out and not implement anything, and instead a T4 version should take it's place.

What I have not done is if there are any improvements, changes to the top level ADC (ADCL) object, I did not merge those into the ADC. In the hope that most of the T4 functionality is captured within the module.

I was able to run the readPin test sketch and it appeared at the surface appear to be doing something...

I also hacked up the DMA RMS example I have in ADCL_t4 that uses that class for DMA (which I updated), and it ran... I also hacked it up some to run on T3.6 board...
Code:
/*
    Warning this is T4 specific, although not sure how much...
*/

#include <ADC.h>
#include <DMAChannel.h>
#include <AnalogBufferDMA.h>

const int readPin = 15;
// optional to define second pin
#define PROCECESS_2_PINS
const int readPin2 = 16;

ADC *adc = new ADC(); // adc object
extern void dumpDMA_TCD(DMABaseClass *dmabc);

// Going to try two buffers here  using 2 dmaSettings and a DMAChannel

const uint32_t buffer_size = 1600;
DMAMEM static volatile uint16_t __attribute__((aligned(32))) dma_adc_buff1[buffer_size];
DMAMEM static volatile uint16_t __attribute__((aligned(32))) dma_adc_buff2[buffer_size];
AnalogBufferDMA abdma1(dma_adc_buff1, buffer_size, dma_adc_buff2, buffer_size);

#ifdef PROCECESS_2_PINS
DMAMEM static volatile uint16_t __attribute__((aligned(32))) dma_adc2_buff1[buffer_size];
DMAMEM static volatile uint16_t __attribute__((aligned(32))) dma_adc2_buff2[buffer_size];
AnalogBufferDMA abdma2(dma_adc2_buff1, buffer_size, dma_adc2_buff2, buffer_size);
#endif

void setup() {
  while (!Serial && millis() < 5000) ;

  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(readPin, INPUT); //pin 23 single ended

  Serial.begin(9600);
  Serial.println("Setup ADC_0");
  // reference can be ADC_REFERENCE::REF_3V3, ADC_REFERENCE::REF_1V2 (not for Teensy LC) or ADC_REF_EXT.
  //adc->setReference(ADC_REFERENCE::REF_1V2, ADC_0); // change all 3.3 to 1.2 if you change the reference to 1V2

  adc->setAveraging(8); // set number of averages
  adc->setResolution(12); // set bits of resolution


  // always call the compare functions after changing the resolution!
  //adc->enableCompare(1.0/3.3*adc->getMaxValue(ADC_0), 0, ADC_0); // measurement will be ready if value < 1.0V
  //adc->enableCompareRange(1.0*adc->getMaxValue(ADC_1)/3.3, 2.0*adc->getMaxValue(ADC_1)/3.3, 0, 1, ADC_1); // ready if value lies out of [1.0,2.0] V

  // enable DMA and interrupts
  Serial.println("before enableDMA"); Serial.flush();


  // setup a DMA Channel.
  // Now lets see the different things that RingbufferDMA setup for us before
  abdma1.init(adc, ADC_0);
#ifdef PROCECESS_2_PINS
  Serial.println("Setup ADC_1");
  adc->setAveraging(8, ADC_1); // set number of averages
  adc->setResolution(12, ADC_1); // set bits of resolution
  abdma2.init(adc, ADC_1);
  adc->startContinuous(readPin2, ADC_1);
#endif

  // Start the dma operation..
  adc->startContinuous(readPin, ADC_0);

  Serial.println("End Setup");
}

char c = 0;

int average_value = 2048;

void loop() {

  // Maybe only when both have triggered?
#ifdef PROCECESS_2_PINS
  if ( abdma1.interrupted() && abdma2.interrupted()) {
    if (abdma1.interrupted()) ProcessAnalogData(&abdma1, 0);
    if (abdma2.interrupted()) ProcessAnalogData(&abdma2, 1);
    Serial.println();
  }
#else
  if ( abdma1.interrupted()) {
    ProcessAnalogData(&abdma1, 0);
    Serial.println();
  }
#endif

}

void ProcessAnalogData(AnalogBufferDMA *pabdma, int8_t adc_num) {
  uint32_t sum_values = 0;
  uint16_t min_val = 0xffff;
  uint16_t max_val = 0;

  volatile uint16_t *pbuffer = pabdma->bufferLastISRFilled();
  volatile uint16_t *end_pbuffer = pbuffer + pabdma->bufferCountLastISRFilled();

  float sum_delta_sq = 0.0;
  if ((uint32_t)pbuffer >= 0x20200000u)  arm_dcache_delete((void*)pbuffer, sizeof(dma_adc_buff1));
  while (pbuffer < end_pbuffer) {
    if (*pbuffer < min_val) min_val = *pbuffer;
    if (*pbuffer > max_val) max_val = *pbuffer;
    sum_values += *pbuffer;
    int delta_from_center = (int) * pbuffer - average_value;
    sum_delta_sq += delta_from_center * delta_from_center;

    pbuffer++;
  }

  int rms = sqrt(sum_delta_sq / buffer_size);
  Serial.printf(" %d - %u(%u): %u <= %u <= %u %d ", adc_num, pabdma->interruptCount(), pabdma->interruptDeltaTime(), min_val,
                sum_values / buffer_size, max_val, rms);
  pabdma->clearInterrupt();
}

I am trying to decide if I should take it any farther, or wait to see how @pedvide progresses. I will sync up to his version and see if any of our examples work.

Then there is the issue of how he would want to integrate any of the extensions we have done that are T4 specific, like chaining of ADC pins, like using QTimer, ADC_ETC, ...
 
@KurtE
Still confused. Got your example to compile by using this branch: https://github.com/KurtE/ADC/tree/T4. The Teensy4 branch doesn't have the your dmabuffer.h/.cpp. But when I use my compare example:
Code:
#if !defined(__IMXRT1062__)  // Teensy 4.x
#error "Only runs on T4"
#endif
#include <ADC.h>

ADC *adc = new ADC(); // adc object

uint8_t pin_cmp = 15;
int8_t resolution = 12;

void setup() {
  // put your setup code here, to run once:
  while (!Serial && millis() < 5000) ;
  Serial.begin(115200);
  Serial.println("Quick and dirty T4 Analog compare stuff");

  
  adc->setResolution(resolution, 0);
  //since cmp pins are on adc4 we have to change the resoultion
  // measurement will be ready if value < 1.0V
  adc->enableCompare(1.0/3.3*adc->getMaxValue(ADC_0), 1, ADC_0); 
  
  // ready if value lies out of [1.0,2.0] V
  //adc->enableCompareRange(1.0*adc->getMaxValue(ADC_1)/3.3, 2.0*adc->getMaxValue(ADC_1)/3.3, 0, 0, ADC_0); 
}

void loop() {
  // put your main code here, to run repeatedly:
  int value;
  //value = adc->analogRead(15);
  //value = adc->analogRead(pin_cmp, 0, 0);
  adc->startSingleRead(pin_cmp, 0);
  while (adc->isConverting(0) ) ;
  value = adc->readSingle(0);

  if(adc->adc0->fail_flag == ADC_ERROR::TIMEOUT) {
    //Serial.println("Some other error happened when comparison should have succeeded.");
    //adc->adc0->printError();
  } else {
    Serial.println(value);
  }
  adc->adc0->resetError();

  delay(50);
}
I am getting this compile error:
Code:
F:\arduino-1.8.10\hardware\teensy\avr\libraries\ADC/ADC.cpp:449: undefined reference to `ADC_Module::enableCompare(short, bool)'
F:\arduino-1.8.10\hardware\teensy\avr\libraries\ADC/ADC.cpp:443: undefined reference to `ADC_Module::enableCompare(short, bool)'
Basically its trying to use the enableCompare in the ADC.cpp and not the adc_module_t4.cpp?
 
@mjs513...

Yep - We have not migrated that one over yet...
In particular currently in the ADCL_t4 library, support for this functionality is in ADCL_t4.cpp...

Code:
void ADCL::enableCompare(int16_t compValue, bool greaterThan, int8_t adc_num)
{
    uint32_t tmp32;

	if(adc_num == 0){
		tmp32 = ADC1_GC & ~(ADC_GC_ACFE_MASK | ADC_GC_ACFGT_MASK | 		ADC_GC_ACREN_MASK);
		/* Enable the feature. */
		tmp32 |= ADC_GC_ACFE_MASK;
		
		if(greaterThan == true)
				tmp32 |= ADC_GC_ACFGT_MASK;
				
		ADC1_GC = tmp32;

		/* Load the compare values. */
		tmp32 = ADC_CV_CV1(compValue);
		ADC1_CV = tmp32;	
	} else {
		tmp32 = ADC2_GC & ~(ADC_GC_ACFE_MASK | ADC_GC_ACFGT_MASK | 		ADC_GC_ACREN_MASK);
		/* Enable the feature. */
		tmp32 |= ADC_GC_ACFE_MASK;
		
		if(greaterThan == true)
				tmp32 |= ADC_GC_ACFGT_MASK;
				
		ADC2_GC = tmp32;

		/* Load the compare values. */
		tmp32 = ADC_CV_CV1(compValue) | ADC_CV_CV1(compValue);
		ADC2_CV = tmp32;	
	}

}

Where I/we need to change this to move this functionality into the module file...
As the top level ADC module just does:
Code:
void ADC::enableCompare(int16_t compValue, bool greaterThan, int8_t adc_num) {
    if(adc_num==1){ // user wants ADC 1, do nothing if it's a Teensy 3.0
        #if ADC_NUM_ADCS>=2 // Teensy 3.1
        adc1->enableCompare(compValue, greaterThan);
        #else
        adc0->fail_flag |= ADC_ERROR::WRONG_ADC;
        #endif
        return;
    }
    adc0->enableCompare(compValue, greaterThan);
    return;
}
And My module one I added a bunch of the functions to the header file but did not implement....

But the question is, should I just punt now..
If you look at PedVides new Teensy4 branch you will see:
Code:
void ADC_Module::enableCompare(int16_t compValue, bool greaterThan) {

    if (calibrating) wait_for_cal(); // if we modify the adc's registers when calibrating, it will fail

    // ADC_SC2_cfe = 1; // enable compare
    // ADC_SC2_cfgt = (int32_t)greaterThan; // greater or less than?
    #ifdef ADC_TEENSY_4
    atomic::setBitFlag(adc_regs.GC, ADC_GC_ACFE);
    atomic::changeBitFlag(adc_regs.GC, ADC_GC_ACFGT, ADC_GC_ACFGT*greaterThan);
    adc_regs.CV = ADC_CV_CV1(compValue);
    #else
    atomic::setBitFlag(adc_regs.SC2, ADC_SC2_ACFE);
    atomic::changeBitFlag(adc_regs.SC2, ADC_SC2_ACFGT, ADC_SC2_ACFGT*greaterThan);
    
    adc_regs.CV1 = (int16_t)compValue; // comp value
    #endif
}
 
FYI - Your test sketch does not compile on his branch as there is no such define as TIMEOUT as an error...
 
@KurtE
Based on what PedVides doing with the lib think I am just going to punt on playing around anymore with the ADC_t4 (you know I am going to probably still clean up that one function call though) since I am not really sure where he is going to go with the rest of the dev work. Seems redundant for us try and combine libs when he is doing it as well.

Oh - did try your DMA example with my joystick and its working like a charm.

On a side note is your noSPIIN ili9341 lib working - was thinking about pulling it down and giving it a test run :)
 
Status
Not open for further replies.
Back
Top