Teensy 4.0 which pins for which ADC

Status
Not open for further replies.
T3.x adc.startPDB converting to T4 and shared resources:

@mjs513, @defragster, @manitou and @Paul - and others...

Will soon look at merging the ADCL_t4 stuff into ADC library... But first want to play some with how to translate the ADC library code that does Analog reads at a specific frequency:
like my well monitoring stuff sets up DMA on both ADC channels (T3.6) and I have an interval timer setup that gets N samples at a rate of 3000 times per second... So at each interval I do something like:

Code:
  _adc0_busy = true;
  _adc1_busy = true;
  _adc0_dma.enable();
  _adc1_dma.enable();
  adc->enableDMA(ADC_0);
  adc->enableDMA(ADC_1);
  _adc0_dma.enable();
  _adc1_dma.enable();
  adc->adc0->startPDB(60 * 50);
  adc->adc1->startPDB(60 * 50);

Luckily @manitou has examples of parts of this for T4, from me to start from (Thanks!) and again I have mentioned parts of this earlier in thread.
for now ignore DMA... The example timed sketch sets up

ADC1 to use hardware trigger with channel 16 (which maps to ADC_ETC)

ADC_ETC0 is setup with -1 chain and trigger 0 and ...

XBAR is setup as glue that says map PIT trigger 0 to ADC_ETC (XBARA1_IN_PIT_TRIGGER0, XBARA1_OUT_ADC_ETC_TRIG00)

Then PIT0 is setup to run at the speed we desire...

(Clear as MUD) Which for my own simple code I can hopefully reasonably easy hack. But more fun will be how to make it such that it works well with other libraries and system code...

Example which timer to use? Example used PIT0, which the system(library) code IntervalTimer uses and sets it's own ISR handler and when you do a begin (for the first time per object) it finds the first free channel of which there are only 4... Could either try to use IntervalTimer code or simply make sure to check/mark the channels. (TCTRL != 0) so that they don't grab same resource.

Should we instead try different timer? QTIMER? which most can be used by PWM, not sure if some of the unused ones could be used, i.e. can they be initialized in a way that does not interfere with PWM
(example I dont think any of QTIMER4 is used, nor 1-3, or 2-1, 2-2, 2-3?
QT4-2 looks like it can optionally used on pin 9...

ADC_ETC: how to choose the different trigger? i.e. should I just pick a specific one for ADC1 and another one for ADC2?

XBAR: I think this sort of takes care of itself, that is you use the register number in it of the OUTPUT Number/2 (either top or bottom half) and value is the input number, so as long as we can pick the proper timer and proper ADC_ETC then no conflict...

But again the more interesting question is how to manage usage of some of the system resources, like Timers, like ADC_ETC Channels (might be more interesting later with touch screen stuff (TSC) I think I would need to read that chapter a few more times (maybe 10)...

I have same question about other resources like FlexIO... In my flexIO library I have some classes to try to manage the timers, shifters, ... But that only works if you use my library...

Well now back to contemplating...
 
Up to about 5 times on ADC_ETC but now have decided to go back to reading the ADC chapter as well now that I looked at ADC_ETC and @manitou's code at the same time - now you know why I have a headache.

Timer: Think you may want to avoid interval timer, a lot of other libraries use that as well and could cause conflicts. QTIMER/PIT might be better choices.

XBAR: Yeah. Once you decide on the timer the rest is relatively easy - that is to configure XBAR.

ADC_ETC: Besides the timers supposedly ADC_ETC has the ability to use a software trigger. Haven't been able that to work yet - missing something - probably because I have no idea what I am doing. ADC_ETC has seems has lots of channels/input options. To make life simpler may want fix the pairs for ADC_ETC, A0/A1, A2/A3 etc. May have to something similar to what you did in the FlexIO library for ADC_ETC if we can figure it out.

Ok back to other diversions.....
 
@mjs513 - Yep but issue is IntervalTimer is PIT timer, so could do similar code to try to grab one of them.

I might be missing something, but I see we have GPT, PIT, and Quad Timer as three different timers... Not sure about GPT - but I don't see any XBAR input values...
 
@KurtE

Just looked myself and concur, didn't see XBAR for GPT. Didn't see anything in the manual either, at least that I could find.
 
@mjs513 - I have been hacking up a quick and dirty test version of @manitou's test program that is PIT0 with XBAR to try to use QTimer4-0
Looks like only Qtimer3 and 4 have qbara1_in values...

I am using the core\teensy4\pwm.c functions to help out...
Code:
// from SDK PIT XBAR ADC_ETC ADC
// chain A0 A1   AD_B1_02  AD_B1_03   ADC1 IN7 8
#define PRREG(x) Serial.print(#x" 0x"); Serial.println(x,HEX)
//#define USE_PIT0
//#define USE_


volatile uint32_t val0, val1;

// 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 adcetc0_isr() {
  digitalWriteFast(13, !digitalReadFast(13));
  ADC_ETC_DONE0_1_IRQ |= 1;   // clear
  val0 = ADC_ETC_TRIG0_RESULT_1_0 & 4095;
  asm("dsb");
}

void adcetc1_isr() {
  ADC_ETC_DONE0_1_IRQ |= 1 << 16;   // clear
  val1 = (ADC_ETC_TRIG0_RESULT_1_0 >> 16) & 4095;
  asm("dsb");
}

void adc_init() {
  // init and calibrate with help from core
  analogReadResolution(12);
  analogRead(0);
  analogRead(1);
  ADC1_CFG |= ADC_CFG_ADTRG;   // hardware trigger
  // ADC1_CFG = 0x200b;
  ADC1_HC0 = 16;   // ADC_ETC channel
  ADC1_HC1 = 16;
}

void adc_etc_init() {
  ADC_ETC_CTRL &= ~(1 << 31); // SOFTRST
  ADC_ETC_CTRL = 0x40000001;  // start with trigger 0
  ADC_ETC_TRIG0_CTRL = 0x100;   // chainlength -1
  ADC_ETC_TRIG0_CHAIN_1_0 = 0x50283017;   // ADC1 7 8, chain channel, HWTS, IE, B2B
  attachInterruptVector(IRQ_ADC_ETC0, adcetc0_isr);
  NVIC_ENABLE_IRQ(IRQ_ADC_ETC0);
  attachInterruptVector(IRQ_ADC_ETC1, adcetc1_isr);
  NVIC_ENABLE_IRQ(IRQ_ADC_ETC1);
}

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
}

#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

void setup() {
  pinMode(13, OUTPUT);
  Serial.begin(9600);
  while (!Serial);
  delay(1000);
  xbar_init();
  adc_init();
  adc_etc_init();
#ifdef USE_PIT0
  pit_init(24 * 1000000 / 4);
#else
  qtimer_init(20.0);  // try at 20 hz for test...
#endif
  PRREG(ADC1_CFG);
  PRREG(ADC1_HC0);
  PRREG(ADC1_HC1);
  PRREG(ADC_ETC_CTRL);
  PRREG(ADC_ETC_TRIG0_CTRL);
  PRREG(ADC_ETC_TRIG0_CHAIN_1_0);
}

void loop() {
  Serial.printf("%d  %d\n", val0, val1);
  delay(2000);
}

As far as I can tell no one uses Qtimer4-0 or 4-1. 4-2 and 4-3 are used by FreqCountTimers.h..

How is this for hacking ;)
 
@KurtE

Not bad. pwm.c is exactly where I got the xbar connect functions for the encoder example that I used :) Will have to give it a try.

Question though. In the ADC_ETC portion of the RM is am seeing things like "1'b1" in the register settings. Example;
Code:
-0: Trig enable register, 1'b1: enable corresponding ext XBAR trig
never saw that format before. Can you translate?
 
Question though. In the ADC_ETC portion of the RM is am seeing things like "1'b1" in the register settings. Example;
Code:
-0: Trig enable register, 1'b1: enable corresponding ext XBAR trig
never saw that format before. Can you translate?
Translate: I think that is called poorly written manual.

I think they are simply saying the field is 1 bit and a value of one is XBar Trigger...

Of course this makes it real fun to read like:
TRIG enable register. 1'b1: enable correspond external XBAR trigger [7 ... 0] . 1'b0: disable correspond
external XBAR trigger [7 ... 0] .
Which again I think this probably means there are 8 bits a value of 1 in the first bit enables trigger 0 a value of 1 in 2nd bit enables trigger 1...
 
@KurtE
The other day I started dissecting @manitou's code against the adc_etc chapter - understand settings against example - yes I am dense, here is what I have so far if you are interested:
Code:
#define ADC_ETC_TRIGn_CHAIN_1_0_HWTS0(x)  (((uint32_t)(((uint32_t)(x)) << 4U)) & 0xFF0U)
#define ADC_ETC_TRIGn_CHAIN_1_0_CSEL0(x)  (((uint32_t)(((uint32_t)(x)) << 0U)) & 0xFU)
#define ADC_ETC_TRIGn_CHAIN_1_0_IE0(x)    (((uint32_t)(((uint32_t)(x)) << 13U)) & 0x6000U)
  
void adcetc0_isr() {
  ADC_ETC_DONE0_1_IRQ |= 1;   // clear
  val0 = ADC_ETC_TRIG0_RESULT_1_0 & adc_resolution;
  asm("dsb");
}

void adcetc1_isr() {
  ADC_ETC_DONE0_1_IRQ |= 1 << 16;   // clear
  val1 = (ADC_ETC_TRIG0_RESULT_1_0 >> 16) & adc_resolution;
  asm("dsb");
}

void adc_init() {
  // init and calibrate with help from core
  analogReadResolution(12);
  analogRead(0);
  analogRead(1);
  ADC1_CFG |= ADC_CFG_ADTRG;   // hardware trigger  from ADC_ETC
  //ADC_CFG_ADTRG = 1<<13 for H/T, 0 is set for S/W Trig
  // ADC1_CFG = 0x200b;
  ADC1_HC0 = 16;   // External channel selection from ADC_ETC
  ADC1_HC1 = 16;

}

void adc_etc_init() {
  ADC_ETC_CTRL &= ~(1 << 31); // SOFTRST
  ADC_ETC_CTRL = 0x40000001;  // start with trigger 0
  //1  soft reset
  //0  TSC_BYPASS
  //0  DMA_MODE_S EL
  //00000    --- reserved
  //0000000  000   0    000     0        00000001
  //23-16: pre-divider for trig delay and interval ---- 0u
  //15-13: Ext TSC1 trig priority, 7 is highest, 0 is lowest
  //12:tsc1 trig enable  false
  //11-9: TSC0 trig priority, 0
  //8: tsc0 enable, 0
  //7-0: Trig enable register, 1'b1: enable corresponding ext XBAR trig
  //     (7...0), 1'b0 disable external xbar trigger = 1u
  //
  

  ADC_ETC_TRIG0_CTRL = 0x100; // chainlength -1, HW trigger
  //16: Sync mode, 0 to disable
  //14-12: trigger priority, 
  //10-8: chain length, 2-1 = 0
  //4: Trig mode = 0 for h/w, 1 s/w
  //1: s/w trigger, write 1
  
  
  ADC_ETC_TRIG0_CHAIN_1_0 = 0x50283017;   // ADC1 7 8, chain channel, HWTS, IE, B2B
  /*
		10(finish interrupt on DONE1)
		1(enable B2B chain1) 
		00000010(Chain1 HWTS1)  
		1000(Pin IN8 chain1)
		
		0(reserved) 
		
		01(finish interrupt on DONE0) 
		1(enable B2B chain0)  
		00000001(Chain0 HWTS0)  ADC hardware trigger selection???
		0111(Pin IN7)
  */

Couple of things I still need to sort out :)
 
@mjs513 I was about to type of something similar... @Paul if you are looking...
Here is the start of what I have:
Code:
#define ADC_ETC_CTRL_SOFTRST        ((uint32_t)(1<<31))
#define ADC_ETC_CTRL_TSC_BYPASS       ((uint32_t)(1<<30))
#define ADC_ETC_CTRL_DMA_MODE_SEL     ((uint32_t)(1<<29))
#define ADC_ETC_CTRL_PRE_DIVIDER(n)     ((uint32_t)(((n) & 0xff) << 16))
#define ADC_ETC_CTRL_EXT1_TRIG_PRIORITY(n)  ((uint32_t)(((n) & 0x07) << 13))
#define ADC_ETC_CTRL_EXT1_TRIG_ENABLE   ((uint32_t)(1<<12))
#define ADC_ETC_CTRL_EXT0_TRIG_PRIORITY(n)  ((uint32_t)(((n) & 0x07) << 9))
#define ADC_ETC_CTRL_EXT0_TRIG_ENABLE   ((uint32_t)(1<<8))
#define ADC_ETC_CTRL_TRIG_ENABLE(n)     ((uint32_t)(((n) & 0xff) << 0))

#define ADC_ETC_DONE0_1_IRQ_TRIG7_DONE1   ((uint32_t)(1<<23))
#define ADC_ETC_DONE0_1_IRQ_TRIG6_DONE1   ((uint32_t)(1<<22))
#define ADC_ETC_DONE0_1_IRQ_TRIG5_DONE1   ((uint32_t)(1<<21))
#define ADC_ETC_DONE0_1_IRQ_TRIG4_DONE1   ((uint32_t)(1<<20))
#define ADC_ETC_DONE0_1_IRQ_TRIG3_DONE1   ((uint32_t)(1<<19))
#define ADC_ETC_DONE0_1_IRQ_TRIG2_DONE1   ((uint32_t)(1<<18))
#define ADC_ETC_DONE0_1_IRQ_TRIG1_DONE1   ((uint32_t)(1<<17))
#define ADC_ETC_DONE0_1_IRQ_TRIG0_DONE1   ((uint32_t)(1<<16))
#define ADC_ETC_DONE0_1_IRQ_TRIG7_DONE0   ((uint32_t)(1<<7))
#define ADC_ETC_DONE0_1_IRQ_TRIG6_DONE0   ((uint32_t)(1<<6))
#define ADC_ETC_DONE0_1_IRQ_TRIG5_DONE0   ((uint32_t)(1<<5))
#define ADC_ETC_DONE0_1_IRQ_TRIG4_DONE0   ((uint32_t)(1<<4))
#define ADC_ETC_DONE0_1_IRQ_TRIG3_DONE0   ((uint32_t)(1<<3))
#define ADC_ETC_DONE0_1_IRQ_TRIG2_DONE0   ((uint32_t)(1<<2))
#define ADC_ETC_DONE0_1_IRQ_TRIG1_DONE0   ((uint32_t)(1<<1))
#define ADC_ETC_DONE0_1_IRQ_TRIG0_DONE0   ((uint32_t)(1<<0))

#define ADC_ETC_DONE2_ERR_TRIG7_ERR     ((uint32_t)(1<<23))
#define ADC_ETC_DONE2_ERR_TRIG6_ERR     ((uint32_t)(1<<22))
#define ADC_ETC_DONE2_ERR_TRIG5_ERR     ((uint32_t)(1<<21))
#define ADC_ETC_DONE2_ERR_TRIG4_ERR     ((uint32_t)(1<<20))
#define ADC_ETC_DONE2_ERR_TRIG3_ERR     ((uint32_t)(1<<19))
#define ADC_ETC_DONE2_ERR_TRIG2_ERR     ((uint32_t)(1<<18))
#define ADC_ETC_DONE2_ERR_TRIG1_ERR     ((uint32_t)(1<<17))
#define ADC_ETC_DONE2_ERR_TRIG0_ERR     ((uint32_t)(1<<16))
#define ADC_ETC_DONE2_ERR_TRIG7_DONE2   ((uint32_t)(1<<7))
#define ADC_ETC_DONE2_ERR_TRIG6_DONE2   ((uint32_t)(1<<6))
#define ADC_ETC_DONE2_ERR_TRIG5_DONE2   ((uint32_t)(1<<5))
#define ADC_ETC_DONE2_ERR_TRIG4_DONE2   ((uint32_t)(1<<4))
#define ADC_ETC_DONE2_ERR_TRIG3_DONE2   ((uint32_t)(1<<3))
#define ADC_ETC_DONE2_ERR_TRIG2_DONE2   ((uint32_t)(1<<2))
#define ADC_ETC_DONE2_ERR_TRIG1_DONE2   ((uint32_t)(1<<1))
#define ADC_ETC_DONE2_ERR_TRIG0_DONE2   ((uint32_t)(1<<0))

#define ADC_ETC_DMA_CTRL_TRIG7_REQ      ((uint32_t)(1<<23))
#define ADC_ETC_DMA_CTRL_TRIG6_REQ      ((uint32_t)(1<<22))
#define ADC_ETC_DMA_CTRL_TRIG5_REQ      ((uint32_t)(1<<21))
#define ADC_ETC_DMA_CTRL_TRIG4_REQ      ((uint32_t)(1<<20))
#define ADC_ETC_DMA_CTRL_TRIG3_REQ      ((uint32_t)(1<<19))
#define ADC_ETC_DMA_CTRL_TRIG2_REQ      ((uint32_t)(1<<18))
#define ADC_ETC_DMA_CTRL_TRIG1_REQ      ((uint32_t)(1<<17))
#define ADC_ETC_DMA_CTRL_TRIG0_REQ      ((uint32_t)(1<<16))
#define ADC_ETC_DMA_CTRL_TRIG7_ENABLE   ((uint32_t)(1<<7))
#define ADC_ETC_DMA_CTRL_TRIG6_ENABLE   ((uint32_t)(1<<6))
#define ADC_ETC_DMA_CTRL_TRIG5_ENABLE   ((uint32_t)(1<<5))
#define ADC_ETC_DMA_CTRL_TRIG4_ENABLE   ((uint32_t)(1<<4))
#define ADC_ETC_DMA_CTRL_TRIG3_ENABLE   ((uint32_t)(1<<3))
#define ADC_ETC_DMA_CTRL_TRIG2_ENABLE   ((uint32_t)(1<<2))
#define ADC_ETC_DMA_CTRL_TRIG1_ENABLE   ((uint32_t)(1<<1))
#define ADC_ETC_DMA_CTRL_TRIG0_ENABLE   ((uint32_t)(1<<0))

#define ADC_ETC_TRIG_CTRL_SYNC_MODE     ((uint32_t)(1<<16))
#define ADC_ETC_TRIG_CTRL_TRIG_PRIORITY(n)  ((uint32_t)(((n) & 0x07) << 12))
#define ADC_ETC_TRIG_CTRL_TRIG_CHAIN(n)   ((uint32_t)(((n) & 0x07) << 8))
#define ADC_ETC_TRIG_CTRL_TRIG_MODE     ((uint32_t)(1<<4))
#define ADC_ETC_TRIG_CTRL_SW_TRIG     ((uint32_t)(1<<0))

#define ADC_ETC_TRIG_COUNTER_SAMPLE_INTERVAL(n) ((uint32_t)(((n) & 0xff) << 16))
#define ADC_ETC_TRIG_COUNTER_INIT_DELAY(n)    ((uint32_t)(((n) & 0xff) << 0))

#define ADC_ETC_TRIG_CHAIN_IE1(n)     ((uint32_t)(((n) & 0x03) << 29))
#define ADC_ETC_TRIG_CHAIN_B2B1       ((uint32_t)(1<<28))
#define ADC_ETC_TRIG_CHAIN_HWTS1(n)     ((uint32_t)(((n) & 0xff) << 20))
#define ADC_ETC_TRIG_CHAIN_CSEL1(n)     ((uint32_t)(((n) & 0x0f) << 16))
#define ADC_ETC_TRIG_CHAIN_IE0(n)     ((uint32_t)(((n) & 0x03) << 13))
#define ADC_ETC_TRIG_CHAIN_B2B0       ((uint32_t)(1<<12))
#define ADC_ETC_TRIG_CHAIN_HWTS0(n)     ((uint32_t)(((n) & 0xff) << 4))
#define ADC_ETC_TRIG_CHAIN_CSEL0(n)     ((uint32_t)(((n) & 0x0f) << 0))
// DO we copy for chain 3-2 ...

#define ADC_ETC_TRIG_RESULT_DATA1(n)    ((uint32_t)(((n) & 0xff) << 16))
#define ADC_ETC_TRIG_RESULT_DATA0(n)    ((uint32_t)(((n) & 0xff) << 0))

But there are a few more things I am wondering about doing...
That is for example: I have for TRIG_CHAIN... The issue is that there are more of these defined, like Trig_chain_2_3... Which is identical to 0_1 except everywhere that has a 0 is now a 2 and everywhere that is a 1 is a 3... likewide for 4-5...

Also wondering if I should make an structure for the adc_etc ADC_ETC_t
Which has array of the ETC_TRIGERs... ...

I also hacked on ADCL_t4 to export the function that maps pin number to channel...

With this, a test app for DMA stuff, the adc_etc_init looks like:
Code:
void adc_etc_init(uint8_t pin) {
  uint8_t adc_pin_channel = ADCL::mapPinToChannel(pin);
  ADC_ETC_CTRL = ADC_ETC_CTRL_SOFTRST; // SOFTRST
  ADC_ETC_CTRL = ADC_ETC_CTRL_DMA_MODE_SEL | ADC_ETC_CTRL_TRIG_ENABLE(1); // 0x40000001;  // start with trigger 0
  ADC_ETC_TRIG0_CTRL = 0;   // chainlength -1
  ADC_ETC_TRIG0_CHAIN_1_0 = ADC_ETC_TRIG_CHAIN_B2B0 
      | ADC_ETC_TRIG_CHAIN_HWTS0(1) | ADC_ETC_TRIG_CHAIN_CSEL0(adc_pin_channel);   // ADC1 7  chain channel, HWTS,  BB? TODO
  //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

  
//  ADC_ETC_DMA_CTRL = 1; // ch 0 enable dma
}

Thoughts?

Edit I think the structure would look something like:
Code:
typedef struct {
	uint32_t DONE0_1_IRQ;				// offset004
	uint32_t DONE2_ERR_IRQ;				// offset008
	uint32_t DMA_CTRL;					// offset00C
	struct {
		uint32_t CTRL;					//offset010
		uint32_t COUNTER;				//offset014
		uint32_t CHAIN_1_0;				//offset018
		uint32_t CHAIN_3_2;				//offset01C
		uint32_t CHAIN_5_4;				//offset020
		uint32_t CHAIN_7_6;				//offset024
		uint32_t RESULT_1_0;			//offset028
		uint32_t RESULT_3_2;			//offset02C
		uint32_t RESULT_5_4;			//offset030
		uint32_t RESULT_7_6;			//offset034
	} TRIG[7];
} IMXRT_ADC_ETC_t;
 
Last edited:
@KurtE
Yep - I extracted all the ADC_ETC defines from the sdk to look at in conjunction with their example and @manitou's. I have that list if you want it - its rather long the way they have. Anyway now you know why I said I had a headache looking at it. Just seems a lot of redundancy but guess can't be avoided.

Also wondering if I should make an structure for the adc_etc ADC_ETC_t
Which has array of the ETC_TRIGERs... ...
might be better approach and make at some of it shorter.

Code:
I also hacked on ADCL_t4 to export the function that maps pin number to channel...
channel was what I was getting stuck on now.

Code:
With this, a test app for DMA stuff, the adc_etc_init looks like:
looking good. Going to attach a zip that might be helpful.
 

Attachments

  • New folder (2).zip
    7 KB · Views: 47
@mjs513 Will look through your defines...
Note: I posted back on the new beta build thread, to try to get @Pauls attention.

I think a combination of changes might be good.
As I mentioned in other thread the ADC_ETC data structure I think could be reduced to:
Code:
typedef struct {
	uint32_t DONE0_1_IRQ;				// offset004
	uint32_t DONE2_ERR_IRQ;				// offset008
	uint32_t DMA_CTRL;					// offset00C
	struct {
		uint32_t CTRL;					//offset010
		uint32_t COUNTER;				//offset014
		uint32_t CHAIN_NP1_N[4];
		uint32_t RESULT_NP1_N[4]l
	} TRIG[7];
} IMXRT_ADC_ETC_t;
#define IMXRT_ADC_ETC		(*(IMXRT_ADC_ETC_t *)0x403B0000)
And yes some of the defines like:
#define ADC_ETC_DONE0_1_IRQ_TRIG7_DONE1 ((uint32_t)(1<<23))
#define ADC_ETC_DONE0_1_IRQ_TRIG6_DONE1 ((uint32_t)(1<<22))
#define ADC_ETC_DONE0_1_IRQ_TRIG5_DONE1 ((uint32_t)(1<<21))
#define ADC_ETC_DONE0_1_IRQ_TRIG4_DONE1 ((uint32_t)(1<<20))
#define ADC_ETC_DONE0_1_IRQ_TRIG3_DONE1 ((uint32_t)(1<<19))
#define ADC_ETC_DONE0_1_IRQ_TRIG2_DONE1 ((uint32_t)(1<<18))
#define ADC_ETC_DONE0_1_IRQ_TRIG1_DONE1 ((uint32_t)(1<<17))
#define ADC_ETC_DONE0_1_IRQ_TRIG0_DONE1 ((uint32_t)(1<<16))
#define ADC_ETC_DONE0_1_IRQ_TRIG7_DONE0 ((uint32_t)(1<<7))
#define ADC_ETC_DONE0_1_IRQ_TRIG6_DONE0 ((uint32_t)(1<<6))
#define ADC_ETC_DONE0_1_IRQ_TRIG5_DONE0 ((uint32_t)(1<<5))
#define ADC_ETC_DONE0_1_IRQ_TRIG4_DONE0 ((uint32_t)(1<<4))
#define ADC_ETC_DONE0_1_IRQ_TRIG3_DONE0 ((uint32_t)(1<<3))
#define ADC_ETC_DONE0_1_IRQ_TRIG2_DONE0 ((uint32_t)(1<<2))
#define ADC_ETC_DONE0_1_IRQ_TRIG1_DONE0 ((uint32_t)(1<<1))
#define ADC_ETC_DONE0_1_IRQ_TRIG0_DONE0 ((uint32_t)(1<<0))
Could/should be reduced down to something like:
Code:
#define ADC_ETC_DONE1(n)   ((uint32_t)(1<<(16 + ((n) &0x7)))
#define ADC_ETC_DONE0(n)   ((uint32_t)(1<<((n) &0x7)))
 
@KurtE
Figured out the last piece of the ADC_ETC_TRIG0_CHAIN_1_0 register :)
Code:
  ADC_ETC_TRIG0_CHAIN_1_0 = 0x50283017;   // ADC1 7 8, chain channel, HWTS, IE, B2B
  /*
		10(finish interrupt on DONE1)
		1(enable B2B chain1) 
		[COLOR="#FF0000"]00000010(Chain1 HWTS1)  using HC1[/COLOR]
		1000(Pin IN8 chain1)
		
		0(reserved) 
		
		01(finish interrupt on DONE0) 
		1(enable B2B chain0)  
		[COLOR="#FF0000"]00000001(Chain0 HWTS0)  ADC hardware trigger selection???  
								DEMO_ADC_CHANNEL_GROUP=0;  Select ADC_HC0 register to trigger. max 8[/COLOR]
		0111(Pin IN7)
  */
Just in case you are interested
 
@mjs513 and ...

I think I am making some progress...

I pushed up a new branch of the core project: https://github.com/KurtE/cores/tree/T4_IMXRT_ADC_ETC
Which has structure for the ADC_ETC defined, plus defines for fields and the like.

Would be interesting to get suggestions on it. I went with arrays of stuff like:
Code:
// 66.5.1 Page 3504
typedef struct {
  uint32_t CTRL;              // offset 0
  uint32_t DONE0_1_IRQ;       // offset004
  uint32_t DONE2_ERR_IRQ;     // offset008
  uint32_t DMA_CTRL;          // offset00C
  struct {
    uint32_t CTRL;            //offset010
    uint32_t COUNTER;         //offset014
    uint32_t CHAIN_NP1_N[4];
    uint32_t RESULT_NP1_N[4];
  } TRIG[7];
} IMXRT_ADC_ETC_t;
Not sure about NP1_N (N Plus 1 and N).
Example CHAIN_NP1_N, some alternative names might be: CHAIN10 or just CHAIN...

Could also remove these arrays and have it like:
Code:
typedef struct {
  uint32_t CTRL;              // offset 0
  uint32_t DONE0_1_IRQ;       // offset004
  uint32_t DONE2_ERR_IRQ;     // offset008
  uint32_t DMA_CTRL;          // offset00C
  struct {
    uint32_t CTRL;            //offset010
    uint32_t COUNTER;         //offset014
    uint32_t CHAIN_1_0;
    uint32_t CHAIN_3_2;
    uint32_t CHAIN_5_4;
    uint32_t CHAIN_7_6;
    uint32_t RESULT_1_0;
    uint32_t RESULT_3_2;
    uint32_t RESULT_5_4;
    uint32_t RESULT_7_6;
  } TRIG[7];
} IMXRT_ADC_ETC_t;

I also pushed up some changes to ADCL_t4, that allow me to use the function to map pin number to ADC channel.

Also to allow me to muck with my DMA class to so far change the DMAMUX_SOURCE ...

I now have an example that is showing some DMA outputs, although I think the timings are all screwed up...
Code:
/*
    Warning this is T4 specific, although not sure how much...

    This one is really hacked up to try to use qtimer0/XBar/ADC_ETC to
    control the timing of the query!
*/

#include <ADCL_t4.h>
#include <DMAChannel.h>
#include <AnalogBufferDMA.h>
//#include "imxrt_additions.h"

#define USE_TIMED_READS
//#define USE_PIT0

const int readPin = 15;
ADCL *adc = new ADCL(); // 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);

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, DMAMUX_SOURCE_ADC_ETC);
  Serial.println("After enableDMA"); Serial.flush();

  // Start the dma operation..
#ifdef USE_TIMED_READS
  setup_adc_hardware_trigger(readPin, &abdma1);
#else
  adc->analogRead(readPin, ADC_0);
#endif
  Serial.println("End Setup");
}

char c = 0;

int average_value = 2048;

void loop() {

  // Maybe only when both have triggered?
  if ( abdma1.interrupted()) {
    ProcessAnalogData(&abdma1, 0);
    Serial.println();
  }
}

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();
}

#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_GC &= ~ADC_GC_ADCO;
}

void adc_etc_init(uint8_t pin) {
  uint8_t adc_pin_channel = ADCL::mapPinToChannel(pin);
  IMXRT_ADC_ETC.CTRL = ADC_ETC_CTRL_SOFTRST; // SOFTRST
  IMXRT_ADC_ETC.CTRL &= ~ADC_ETC_CTRL_SOFTRST; // SOFTRST
  delay(5);
  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(0);   // chainlength -1 only us
  IMXRT_ADC_ETC.TRIG[0].CHAIN_NP1_N[0] =
    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) ;

  IMXRT_ADC_ETC.DMA_CTRL = ADC_ETC_DMA_CTRL_TRIQ_ENABLE(0);

  //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
}

#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
void update_dma_settings(AnalogBufferDMA *pabdma)
{
  // Lets muck with the dma structure we setup earlier....
  // for now lets assume ADC1 (ADC_0) // I think R0 is fine?
#if 0
  pabdma->_dmasettings_adc[0].source((volatile uint16_t&)(ADC1_R0));
  pabdma->_dmasettings_adc[1].source((volatile uint16_t&)(ADC1_R0));
  pabdma->_dmachannel_adc = pabdma->_dmasettings_adc[0];
  // Lets update the event...
  pabdma->_dmachannel_adc.disable();
  pabdma->_dmachannel_adc.triggerAtHardwareEvent(DMAMUX_SOURCE_ADC_ETC);
  pabdma->_dmachannel_adc.enable();
#endif
}


void setup_adc_hardware_trigger(uint8_t pin, AnalogBufferDMA *pabdma)
{

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

  update_dma_settings(pabdma);

  Serial.println("After UPDATE DMA"); Serial.flush();
#ifdef USE_PIT0
  pit_init(24 * 1000);   // 1 khz  1000 ADCs/sec
#else
  qtimer_init(300.0);  // try at 20 hz for test...
#endif

  // Lets again try dumping lots of data.
  dumpDMA_TCD(&(pabdma->_dmachannel_adc));
  dumpDMA_TCD(&(pabdma->_dmasettings_adc[0]));
  dumpDMA_TCD(&(pabdma->_dmasettings_adc[1]));
  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);
}
#endif

I only have one channel defined here:
Code:
Setup ADC_0

before enableDMA

before enableDMA

20001220 400e9000:SA:400c4024 SO:0 AT:101 NB:2 SL:0 DA:20200000 DO: 2 CI:640 DL:20001200 CS:12 BI:640
200011a0 200011c0:SA:400c4024 SO:0 AT:101 NB:2 SL:0 DA:20200000 DO: 2 CI:640 DL:20001200 CS:12 BI:640
200011e0 20001200:SA:400c4024 SO:0 AT:101 NB:2 SL:0 DA:20200c80 DO: 2 CI:640 DL:200011c0 CS:12 BI:640
ADC1: HC0:1f HS:1 CFG:47bb GC:62 GS:0
After enableDMA

After XBAR

ADC_ETC_CTRL: 40000001 1
After ADC/ADC_ETC

After UPDATE DMA

Try to init QTimer

After Qtimer init

20001220 400e9000:SA:400c4024 SO:0 AT:101 NB:2 SL:0 DA:20200000 DO: 2 CI:640 DL:20001200 CS:12 BI:640
200011a0 200011c0:SA:400c4024 SO:0 AT:101 NB:2 SL:0 DA:20200000 DO: 2 CI:640 DL:20001200 CS:12 BI:640
200011e0 20001200:SA:400c4024 SO:0 AT:101 NB:2 SL:0 DA:20200c80 DO: 2 CI:640 DL:200011c0 CS:12 BI:640
ADC1: HC0:10 HS:0 CFG:67bb GC:22 GS:0
ADC_ETC: CTRL:40000001 DMA: 1 TRIG0: CTRL: 0 CHAIN01:3018
End Setup

 0 - 1(7): 2065 <= 2065 <= 2065 17 

 0 - 5(1): 2065 <= 2065 <= 2065 17 

 0 - 12(1): 2065 <= 2065 <= 2065 17 

 0 - 19(1): 2065 <= 2065 <= 2065 17 

 0 - 25(1): 2064 <= 2064 <= 2064 16 

 0 - 32(1): 2064 <= 2064 <= 2064 16
The last change the started things to output was to enable the DMA channel
IMXRT_ADC_ETC.DMA_CTRL = ADC_ETC_DMA_CTRL_TRIQ_ENABLE(0);


Now to more debugging
 
Timing I think fixed now: :D or at least a lot better!

Need to turn on DMA Mode:
Code:
  IMXRT_ADC_ETC.CTRL = (ADC_ETC_CTRL_TSC_BYPASS | [COLOR="#FF0000"]ADC_ETC_CTRL_DMA_MODE_SEL [/COLOR]| ADC_ETC_CTRL_TRIG_ENABLE(1)); // 0x40000001;  // start with trigger 0
 
@KurtE
As much as I hate to say it, think I prefer the struct like this:
Code:
typedef struct {
  uint32_t CTRL;              // offset 0
  uint32_t DONE0_1_IRQ;       // offset004
  uint32_t DONE2_ERR_IRQ;     // offset008
  uint32_t DMA_CTRL;          // offset00C
  struct {
    uint32_t CTRL;            //offset010
    uint32_t COUNTER;         //offset014
    uint32_t CHAIN_1_0;
    uint32_t CHAIN_3_2;
    uint32_t CHAIN_5_4;
    uint32_t CHAIN_7_6;
    uint32_t RESULT_1_0;
    uint32_t RESULT_3_2;
    uint32_t RESULT_5_4;
    uint32_t RESULT_7_6;
  } TRIG[7];
} IMXRT_ADC_ETC_t;
at least its clearer on what elements you are using.

No comment on forgetting to enable things - done that a few dozen times myself :)

Now to get the branch.
 
@KurtE

A couple of questions. Your example is just testing using a HW trigger on a single pin of the chain - ie, 0 of 1_0? I think. If that is the case it seems to be working no problem and you got the code broken.

Other thing I noticed is that if I use your updated IMXRT.h file it breaks the pitxbaradc.ino example that @manitou put together?
 
@mjs513 - I was alternating back and forth between the two structures. I will probably go ahead and convert...

If I do, should I also duplicate all of the defines for any fields within each... Maybe...

As for breaking pitxbaradc.ino example, will have to take a look. I was having some issues with some of his examples sometimes working and sometimes not.
I was finding that the top level IMXRT_ETC_CTRL register was not properly being updated... But adding a delay after turning off the Soft Reset helped.

Code:
  IMXRT_ADC_ETC.CTRL &= ~ADC_ETC_CTRL_SOFTRST; // SOFTRST
  delay(5);
  IMXRT_ADC_ETC.CTRL = (ADC_ETC_CTRL_TSC_BYPASS | ADC_ETC_CTRL_DMA_MODE_SEL | ADC_ETC_CTRL_TRIG_ENABLE(1)); // 0x40000001;  // start with trigger 0

Yep - I think having the ability to have two different timings could be useful, and the example code for ADC using PDB looks like you can, but it is interesting in the descriptions in the first post of the ADC thread:
Code:
Periodic conversions

There are two ways to do periodic conversions: using an IntervalTimer or using the PDB.

It's possible (and easy) to use the IntervalTimer library with this library, see analogReadIntervalTimer.ino in the examples folder.

PDB is supported on both ADCs, see the adc_pdb.ino example. The same frequency is used for both ADCs, unlike using IntervalTimers.

Yet the example shows you passing in the frequency to both starts of the PDB...

So I may have to experiment...

Yes the current DMA example uses HW trigger for single pin of the chain... Using one TImer (QTIMER 4-0)

Then once I get the edit changes done and things working again, may see about two alternatives. Simply adding the two pins in to CHAIN_1_0
And/Or try to setup a second hopefully independent trigger/chain to go to ADC_1 (which in our case is ADC2...)
 
Quick update: Updated structure like you mentioned and updated my sketch...
 

Attachments

  • T4_ADC_DMA_QTIMER_RMS-191217a.zip
    3.9 KB · Views: 47
@KurtE
Now that I figured out what was going with the FIFO on the MPU9250 had no more excuses not to try ADCL again :)

I modified adc_init and adc_etc_int so I can use pins 14 and 15:
Code:
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;
}

void adc_etc_init(uint8_t pin) {
  uint8_t adc_pin_channel = ADCL::mapPinToChannel(pin);
  IMXRT_ADC_ETC.CTRL = ADC_ETC_CTRL_SOFTRST; // SOFTRST
  IMXRT_ADC_ETC.CTRL &= ~ADC_ETC_CTRL_SOFTRST; // SOFTRST
  delay(5);
  IMXRT_ADC_ETC.CTRL = (ADC_ETC_CTRL_TSC_BYPASS | 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[0].CTRL = ADC_ETC_TRIG_CTRL_TRIG_CHAIN(0);   // 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) ;

  IMXRT_ADC_ETC.DMA_CTRL = ADC_ETC_DMA_CTRL_TRIQ_ENABLE(0);
  
  adc_pin_channel = ADCL::mapPinToChannel(readPin2);
  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(readPin2); 

  //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
}
Not sure I did it right and how to modify your dma stuff to get the two pins and not just the one.
 
Good Morning!

I am not sure if I have had enough coffee yet, to look straight at ADC stuff especially DMA.

Changing pins, should be hopefully easy with my later sketch which used the ADC library to convert the pin number to channel. So as long as the new pin is also valid on ADC1(ADC_0), than it should hopefully take care of itself... BUT...

A few of the many things I don't fully understand in the code/reference docs and maybe the order that I might try to tackle

a) Suppose you wish to change the code to a pin that is only supported by ADC2(ADC_1), like: 26/A12? What do I need to change in ADC/ADC_ETC/... to work only with ADC2?

b) DMA stuff with same qtimer - I assume same ADC_ETC entry? Probably chained 0_1. Can we use the same Not sure what all else. XBAR the same?

I started on this, but got distracted - Playing with RA8875 board... Hopefully not too much differences.

c) DMA stuss with different timer? - Currently I am using QT4_0 as no IO pin can use this one. I do see QT4_1, QT4_2 are on IO pins, but not used by PWM, but are used for some capture code (at least one of them)... So could also maybe safely try QT4_3... But with two timers. My guess is this will add additional things to update, like another XBAR connection, Probably a second ADC_ETC trigger, ...
Potentially for now I would say probably OK to punt... But that is just me.

Now more coffee!
 
@KurtE
Would like to get the example that manitou put together working on 2 pins chained together then I think we should punt but that is just because I am getting tired and frustrated with trying to understand all the ins and outs of adc_etc…..

Now coffee as you said and other distractions are calling :)
 
@KurtE

Since I couldn't figure out the DMA stuff I hacked up @manitou's interrupt version of for chaining two pins which seems to work:
Code:
#include <ADCL_t4.h>

#define USE_TIMED_READS
#define USE_PIT0

const int readPin = 14;
const int readPin2 = 15;

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;

void adcetc0_isr() {
  digitalWriteFast(LED_BUILTIN, !digitalReadFast(LED_BUILTIN));
  ADC_ETC_DONE0_1_IRQ |= 1;   // clear
  val0 = ADC_ETC_TRIG0_RESULT_1_0 & 4095;
  asm("dsb");
}

void adcetc1_isr() {
  ADC_ETC_DONE0_1_IRQ |= 1 << 16;   // clear
  val1 = (ADC_ETC_TRIG0_RESULT_1_0 >> 16) & 4095;
  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

  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\n", val0, val1);
  delay(2000);
}


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, adcetc1_isr);
  NVIC_ENABLE_IRQ(IRQ_ADC_ETC1);
}

void adc_etc_init(uint8_t pin, uint8_t pin2) {
  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_SOFTRST; // SOFTRST
  IMXRT_ADC_ETC.CTRL &= ~ADC_ETC_CTRL_SOFTRST; // SOFTRST
  delay(5);
  IMXRT_ADC_ETC.CTRL = (ADC_ETC_TRIG_CTRL_SYNC_MODE | 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(1);   // 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) ;

  //IMXRT_ADC_ETC.DMA_CTRL = ADC_ETC_DMA_CTRL_TRIQ_ENABLE(0);
  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, BIN);

  //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

  attachInterruptVector(IRQ_ADC_ETC0, adcetc0_isr);
  NVIC_ENABLE_IRQ(IRQ_ADC_ETC0);
  attachInterruptVector(IRQ_ADC_ETC1, adcetc1_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
 
@KurtE
Ok took a shot in the dark on making one more change besides the pins and it seems to be working now, needs some cleanup to know which pin is read/printed though:
Code:
/*
    Warning this is T4 specific, although not sure how much...

    This one is really hacked up to try to use qtimer0/XBar/ADC_ETC to
    control the timing of the query!
*/

#include <ADCL_t4.h>
#include <DMAChannel.h>
#include <AnalogBufferDMA.h>
//#include "imxrt_additions.h"

#define USE_TIMED_READS
#define USE_PIT0

const int readPin = 14;
const int readPin2 = 15;

ADCL *adc = new ADCL(); // 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);

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

  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(readPin, INPUT); //pin 23 single ended
  pinMode(readPin2, 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, DMAMUX_SOURCE_ADC_ETC);
  Serial.println("After enableDMA"); Serial.flush();

  // Start the dma operation..
#ifdef USE_TIMED_READS
  setup_adc_hardware_trigger(readPin, &abdma1);
#else
  adc->analogRead(readPin, ADC_0);
#endif
  Serial.println("End Setup");
}

char c = 0;

int average_value = 2048;

void loop() {

  // Maybe only when both have triggered?
  if ( abdma1.interrupted()) {
    ProcessAnalogData(&abdma1, 0);
    Serial.println();
  }
}

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();
}

#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;
}

void adc_etc_init(uint8_t pin) {
  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_SOFTRST; // SOFTRST
  IMXRT_ADC_ETC.CTRL &= ~ADC_ETC_CTRL_SOFTRST; // SOFTRST
  delay(5);
  IMXRT_ADC_ETC.CTRL = (ADC_ETC_TRIG_CTRL_SYNC_MODE | ADC_ETC_CTRL_TSC_BYPASS | 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));
[COLOR="#FF0000"]  IMXRT_ADC_ETC.TRIG[0].CTRL = ADC_ETC_TRIG_CTRL_TRIG_CHAIN(1);   // chainlength -1 only us[/COLOR]
  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) ;

  IMXRT_ADC_ETC.DMA_CTRL = ADC_ETC_DMA_CTRL_TRIQ_ENABLE(0);
  
  adc_pin_channel = ADCL::mapPinToChannel(readPin2);
  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, BIN);

  //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
}

#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
void update_dma_settings(AnalogBufferDMA *pabdma)
{
  // Lets muck with the dma structure we setup earlier....
  // for now lets assume ADC1 (ADC_0) // I think R0 is fine?
#if 1
  pabdma->_dmasettings_adc[0].source((volatile uint16_t&)(ADC1_R0));
  [COLOR="#FF0000"]pabdma->_dmasettings_adc[1].source((volatile uint16_t&)(ADC1_R1));[/COLOR]
  pabdma->_dmachannel_adc = pabdma->_dmasettings_adc[0];
  // Lets update the event...
  pabdma->_dmachannel_adc.disable();
  pabdma->_dmachannel_adc.triggerAtHardwareEvent(DMAMUX_SOURCE_ADC_ETC);
  pabdma->_dmachannel_adc.enable();
#endif
}


void setup_adc_hardware_trigger(uint8_t pin, AnalogBufferDMA *pabdma)
{

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

  update_dma_settings(pabdma);

  Serial.println("After UPDATE DMA"); 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

  // Lets again try dumping lots of data.
  dumpDMA_TCD(&(pabdma->_dmachannel_adc));
  dumpDMA_TCD(&(pabdma->_dmasettings_adc[0]));
  dumpDMA_TCD(&(pabdma->_dmasettings_adc[1]));
  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);
}
#endif
Highlighted in red what I changed to finally get it working :)
 
Partially done with distraction #1 and have a "No Spin" version of ili9341_t3n library, still needs more testing (and probably fixing) before I move it into main branch.

@mjs513 - Looks like some of the same things I would expect to change and ran it on my screen and it is showing some output which is great. But I am not sure what it is doing with the second one.

Some of the things I am believing I am needing to do, and have a partial edit, but not complete is:

Code:
/*
    Warning this is T4 specific, although not sure how much...

    This one is really hacked up to try to use qtimer0/XBar/ADC_ETC to
    control the timing of the query!
*/

#include <ADCL_t4.h>
#include <DMAChannel.h>
#include <AnalogBufferDMA.h>
//#include "imxrt_additions.h"

#define USE_TIMED_READS
//#define USE_PIT0

#define readPin    14
#define readPin2  15

ADCL *adc = new ADCL(); // 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 = 900;
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 readPin2
DMAMEM static volatile uint16_t __attribute__((aligned(32))) dma_adc_buff21[buffer_size];
DMAMEM static volatile uint16_t __attribute__((aligned(32))) dma_adc_buff22[buffer_size];
AnalogBufferDMA abdma2(dma_adc_buff21, buffer_size, dma_adc_buff21, 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

  adc->setAveraging(8, ADC_1); // set number of averages
  adc->setResolution(12, ADC_1); // 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, DMAMUX_SOURCE_ADC_ETC);
#ifdef readPin2
  abdma2.init(adc, ADC_1, DMAMUX_SOURCE_ADC_ETC);
#endif
  
  Serial.println("After enableDMA"); Serial.flush();

  // Start the dma operation..
  setup_adc_hardware_trigger(readPin, &abdma1, ADC_0);
#ifdef readPin2
  setup_adc_hardware_trigger(readPin2, &abdma2, ADC_1);
#endif
  Serial.println("End Setup");
}

char c = 0;

int average_value = 2048;

void loop() {

  // Maybe only when both have triggered?
  if ( abdma1.interrupted()) {
    ProcessAnalogData(&abdma1, 0);
    Serial.println();
  }
}

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();
}

#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_GC &= ~ADC_GC_ADCO;
}

void adc_etc_init(uint8_t pin, int8_t adc_num) {
  uint8_t adc_pin_channel = ADCL::mapPinToChannel(pin, adc_num);
  IMXRT_ADC_ETC.CTRL = ADC_ETC_CTRL_SOFTRST; // SOFTRST
  IMXRT_ADC_ETC.CTRL &= ~ADC_ETC_CTRL_SOFTRST; // SOFTRST
  delay(5);
  IMXRT_ADC_ETC.CTRL = (ADC_ETC_CTRL_TSC_BYPASS | 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[0].CTRL = ADC_ETC_TRIG_CTRL_TRIG_CHAIN(0);   // chainlength -1 only us
  IMXRT_ADC_ETC.TRIG[0].CHAIN_1_0 =
    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) ;

  IMXRT_ADC_ETC.DMA_CTRL = ADC_ETC_DMA_CTRL_TRIQ_ENABLE(0);

  //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
}

#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
void update_dma_settings(AnalogBufferDMA *pabdma)
{
  // Lets muck with the dma structure we setup earlier....
  // for now lets assume ADC1 (ADC_0) // I think R0 is fine?
#if 0
  pabdma->_dmasettings_adc[0].source((volatile uint16_t&)(ADC1_R0));
  pabdma->_dmasettings_adc[1].source((volatile uint16_t&)(ADC1_R0));
  pabdma->_dmachannel_adc = pabdma->_dmasettings_adc[0];
  // Lets update the event...
  pabdma->_dmachannel_adc.disable();
  pabdma->_dmachannel_adc.triggerAtHardwareEvent(DMAMUX_SOURCE_ADC_ETC);
  pabdma->_dmachannel_adc.enable();
#endif
}


void setup_adc_hardware_trigger(uint8_t pin, AnalogBufferDMA *pabdma, int8_t adc_num)
{

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

  update_dma_settings(pabdma);

  Serial.println("After UPDATE DMA"); 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

  // Lets again try dumping lots of data.
  dumpDMA_TCD(&(pabdma->_dmachannel_adc));
  dumpDMA_TCD(&(pabdma->_dmasettings_adc[0]));
  dumpDMA_TCD(&(pabdma->_dmasettings_adc[1]));
  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);
}
#endif

You will see some of the stuff under #ifdef readPin2
a) Need 2nd DmaBUffer setup: so I created a abdma2
Note: I think I may want to have options to this object, to allow only one buffer to be passed in, and have it do a disableOnCompletion, and have way to say do it again... (but side problem).

b) Need to init the ADC_1 object

c) Init the 2nd DMA buffer object

And that is really about as far as I got, Yes I have it call off to setup_adc_hardware_trigger on 2nd one, but sure what it will do, i.e should it use as much of the first setup as possible.
That is currently you have update the
Code:
  pabdma->_dmasettings_adc[0].source((volatile uint16_t&)(ADC1_R0));
  pabdma->_dmasettings_adc[1].source((volatile uint16_t&)(ADC1_R1));
But both of these are chained to each other, to not sure what it is doing? That is you setup for R1 of the first ADC?

I am suspecting that I need to change the SOURCE of the 2nd DMA... to ADC2_R0... (which it probably already would be...)

d) I am unclear on the stuff in adc_etc_init (I suspect that assuming only one timer the changes you did, which is to change the chain length from 1 to 2 (ADC_ETC_TRIG_CTRL_TRIG_CHAIN(1) instead of 0)
Plus fill in the stuff on 2nd pin.

e) Does XBar need to be updated? Right now I am using: XBARA1_IN_QTIMER4_TIMER0, XBARA1_OUT_ADC_ETC_TRIG00

.... My head hurts! ;)
 
Status
Not open for further replies.
Back
Top