Teensy 4.0 which pins for which ADC

Status
Not open for further replies.
I have been unable to find a chart that maps out which of the analog read pins is connected to which of the two ADCs on the chip for the Teensy 4.0.
Could anyone help me find this information?

Thank you in advance
 
I extracted this from pg3457-3458 of the IMXRT1060 Ref manual and used the pinouts from page 1 of the T4 Beta Thread: Hope it helps:
Code:
14/A0	AD_B1_02	ADC1_IN7	Analog channel 1 input 7
15/A1	AD_B1_03	ADC1_IN8	Analog channel 1 input 8
16/A2	AD_B1_07	ADC1_IN12	Analog channel 1 input 12
17/A3	AD_B1_06	ADC1_IN11	Analog channel 1 input 11
18/A4	AD_B1_01	ADC1_IN6	Analog channel 1 input 6
19/A5	AD_B1_00	ADC1_IN5	Analog channel 1 input 5
20/A6	AD_B1_10	ADC1_IN15	Analog channel 1 input 15
21/A7	AD_B1_11	ADC 2_IN0	Analog channel 2 input 0
22/A8	AD_B1_08	ADC1_IN13	Analog channel 1 input 13
23/A9	AD_B1_09	ADC1_IN14	Analog channel 1 input 14
---	----		
24/A10	AD_B0_12	ADC2_IN1	Analog channel 2 input 1
25/A11	AD_B0_13	ADC2_IN2	Analog channel 2 input 2
26/A12	AD_B1_14	ADC2_IN3	Analog channel 2 input 3
27/A13	AD_B1_15	ADC2_IN4	Analog channel 2 input 4
 
Last edited:
I might need to double check my spreadsheet, but I have:
Code:
[B]Pin	Name		GPIO	Analog[/B]
 0	AD_B0_03	1.3	
 1	AD_B0_02	1.2	
 2	EMC_04		4.4	
 3	EMC_05		4.5	
 4	EMC_06		4.6	
5	EMC_08		4.8	
6	B0_10		2.10	
7	B1_01		2.17	
8	B1_00		2.16	
 9	B0_11		2.11	
10	B0_00		2.0	
11	B0_02		2.2	
12	B0_01		2.1	
13	B0_03		2.3	
14/A0	AD_B1_02	1.18	[COLOR="#FF0000"]A1:7, A2:7[/COLOR]
15/A1	AD_B1_03	1.19	[COLOR="#FF0000"]A1:8, A2:8[/COLOR]
16/A2	AD_B1_07	1.23	[COLOR="#FF0000"]A1:12, A2:12[/COLOR]
17/A3	AD_B1_06	1.22	[COLOR="#FF0000"]A1:11, A2:11[/COLOR]
18/A4	AD_B1_01	1.17	[COLOR="#FF0000"]A1:6, A2:6[/COLOR]
19/A5	AD_B1_00	1.16	[COLOR="#FF0000"]A1:5, A2:5[/COLOR]
20/A6	AD_B1_10	1.26	[COLOR="#FF0000"]A1:15, A2:15[/COLOR]
21/A7	AD_B1_11	1.27	[COLOR="#FF0000"]A1:0, A2:0[/COLOR]
22/A8	AD_B1_08	1.24	[COLOR="#FF0000"]A1:13, A2:13[/COLOR]
23/A9	AD_B1_09	1.25	[COLOR="#FF0000"]A1:14, A2:14[/COLOR]
---	----	----	------
24/A10	AD_B0_12	1.12	[COLOR="#FF0000"]A1:1[/COLOR]  
25/A11	AD_B0_13	1.13	[COLOR="#FF0000"]A1:2[/COLOR]
26/A12	AD_B1_14	1.30	[COLOR="#FF0000"]A2:3  [/COLOR]
27/A13	AD_B1_15	1.31	[COLOR="#FF0000"]A2:4 [/COLOR] 
28	EMC_32	3.18	
29	EMC_31	4.31	
30	EMC_37	3.23	
31	EMC_36	3.22	
32	B0_12	2.12	
33	EMC_07	4.7	
			
34	SD_B0_03	2.15	
35	SD_B0_02	3.14	
36	SD_B0_01	3.13	
37	SD_B0_00	3.12	
38	SD_B0_05	3.17	
39	SD_B0_04	3.16

Sorry if things don't align properly...

Note: As you can see many of these pins can actually connect to either ADC...

Another side hint is to look at analog.c where it maps pins to channels. Removing the #if 0 stuff... we have:

Code:
const uint8_t pin_to_channel[] = { // pg 482
	7,	// 0/A0  AD_B1_02
	8,	// 1/A1  AD_B1_03
	12,	// 2/A2  AD_B1_07
	11,	// 3/A3  AD_B1_06
	6,	// 4/A4  AD_B1_01
	5,	// 5/A5  AD_B1_00
	15,	// 6/A6  AD_B1_10
	0,	// 7/A7  AD_B1_11
	13,	// 8/A8  AD_B1_08
	14,	// 9/A9  AD_B1_09
	1,	// 24/A10 AD_B0_12 
	2,	// 25/A11 AD_B0_13
	128+3,	// 26/A12 AD_B1_14 - only on ADC2, 3
	128+4,	// 27/A13 AD_B1_15 - only on ADC2, 4
	7,	// 14/A0  AD_B1_02
	8,	// 15/A1  AD_B1_03
	12,	// 16/A2  AD_B1_07
	11,	// 17/A3  AD_B1_06
	6,	// 18/A4  AD_B1_01
	5,	// 19/A5  AD_B1_00
	15,	// 20/A6  AD_B1_10
	0,	// 21/A7  AD_B1_11
	13,	// 22/A8  AD_B1_08
	14,	// 23/A9  AD_B1_09
	1,	// 24/A10 AD_B0_12
	2,	// 25/A11 AD_B0_13
	128+3,	// 26/A12 AD_B1_14 - only on ADC2, 3
	128+4	// 27/A13 AD_B1_15 - only on ADC2, 4
};

But this only shows which ones we use, not which ones we could use... Note the 128+ marks those which we map to ADC2.

Edit: Side note about this above table. It is setup to look like pin 0 is attached as an analog pin, which it is not, instead this is due to the historical way you can call analogRead. That is analogRead(0) is the same thing as analogRead(A0) which in our case is the same as analogRead(14);
 
@KurtE
Your list is actually better than mine - I missed the second ADC assignment when I went through your list. I must have an outdated spreadsheet -the one I have doesn't have the analog pins.

Edit: oops - yes it does but is on the beta 1 tab.
 
Great, thank you both very much.

For the pins that can connect to both ADCs, the decision would be made by choosing an appropriate setting in the code while initializing the pin, correct?
 
I could be wrong, but I believe for those pins which connect to both, it always uses the first one... i.e. the code table in analog.c does not keep mark that it can use either. But since the code for analogRead(pin) does not return until completed, it probably make much difference.

However hopefully at some point the library ADC will be updated to work on T4. But so far I have not seen anything up on the github project showing any progress toward this.
 
I could be wrong, but I believe for those pins which connect to both, it always uses the first one... i.e. the code table in analog.c does not keep mark that it can use either. But since the code for analogRead(pin) does not return until completed, it probably make much difference.

However hopefully at some point the library ADC will be updated to work on T4. But so far I have not seen anything up on the github project showing any progress toward this.

Hi KurtE,

Any idea how to assign pins to a particular ADC, similar to analogRead(XX, ADC_0/ADC_1) in adc.h? I am trying to sample a 15 MHz sine wave using two analog pins and I want them to happen simultaneously. If I understand correctly, analogRead() will be assigned to ADC and it'll take some time to complete before the next analogRead() is performed. I was hoping to use two separate ADCs to do simultaneous measurements.
Code:
    v1 = analogRead(A2);
    v2 = analogRead(A3);
 
> I am trying to sample a 15 MHz sine wave

You will need to use an external ADC with parallel output. Or an external track and hold with periodic sampling.
 
> I am trying to sample a 15 MHz sine wave

You will need to use an external ADC with parallel output. Or an external track and hold with periodic sampling.

Thanks. I'm guessing this is because of the sampling rate limitation? How about for much lower frequencies? Is there a way to assign analog pins to specific ADC? I am yet to try Bumbler's T4 ADC Library V0. Maybe I'll give that a shot.
 
@tsaha - As I have mentioned I do most things in digital, so others maybe can help more.

It would be great if the library ADC was ported over to the T4. But it looks like the owner (pedvide) of it, has not been up on the forum for maybe a year and a half, and the last update to the library was over two years ago...

So again another useful library that has been semi-orphaned.

Again as I mentioned, the default call analogRead(pin) will not return until it is done doing the analog conversion. so assigning to the two different ADCs probably would not help you much here..

Again not sure how hard it would be to setup at least a subset of the ADC library for T4, that allows you to do some of this.
More details about this library up in the thread: https://forum.pjrc.com/threads/25532-ADC-library-update-now-with-support-for-Teensy-3-1

Again I am not sure hard it would be... I might take a quick look to see if at least some of the basics can be ported... But again would be better if someone who really understands and uses Analog stuff would do it!
 
@tasha
Just to let you know the default settings on initialization is to do a 32 point average using 10bit ADC. You might reduce the time by setting analogReadAveraging. Allowable averages are 4, 8, 16 and 32 point averages.

Looked at the ADC library a while ago - complicated for me to modify the whole thing but a subset of the library is probably doing for the T4. But as @KurtE haven't looked at it for awhile.
 
@tasha @mjs513 - I hacked up a version of AnalogRead that allowed me to choose a channel 1 or 2 to do conversion.

Also did a version where I broke it into two parts. First starts conversion, second checks or waits for completion... Parts of it sort of work..

Not sure how reliable...

But if you want to take a look:
 

Attachments

  • T4_ADC_Lite-191202a.zip
    1.7 KB · Views: 147
@KurtE
Thanks. Going to have to update a couple of other functions as well. AnanlogResolution and analogreadaveragering. Just as a note may have to use that other function on reinitialiaztion of changes. Have a couple of other ideas but sick as a dog right now, head in fog argh
 
@KurtE - @Paul

Was going through analog.c again and was thinking since this is a lite version in order support the second ADC would it be better just to update analog.c to support the second adc?
 
I checked and verified that analogReadAveraging(3) will speed things up. The result with analogRead() was 5 usec per sample.

With the right code, I'd expect < 1 usec per sample and 2x faster than that when alternating between two ADCs.

Perhaps others have hard data wrt "how fast is teensy 4 ADC?".
 
@KurtE - @Paul

Was going through analog.c again and was thinking since this is a lite version in order support the second ADC would it be better just to update analog.c to support the second adc?

My quick look through the functions, analogResolution and analogReadAveraging, I thought they were both already handling both ADC1 and ADC2

Example analogReadRes:
Code:
void analogReadRes(unsigned int bits)
{
  uint32_t tmp32, mode;

   if (bits == 8) {
    // 8 bit conversion (17 clocks) plus 8 clocks for input settling
    mode = ADC_CFG_MODE(0) | ADC_CFG_ADSTS(3);
  } else if (bits == 10) {
    // 10 bit conversion (17 clocks) plus 20 clocks for input settling
    mode = ADC_CFG_MODE(1) | ADC_CFG_ADSTS(2) | ADC_CFG_ADLSMP;
  } else {
    // 12 bit conversion (25 clocks) plus 24 clocks for input settling
    mode = ADC_CFG_MODE(2) | ADC_CFG_ADSTS(3) | ADC_CFG_ADLSMP;
  }

  tmp32  = (ADC1_CFG & (0xFFFFFC00));
  tmp32 |= (ADC1_CFG & (0x03));  // ADICLK
  tmp32 |= (ADC1_CFG & (0xE0));  // ADIV & ADLPC

  tmp32 |= mode; 
  ADC1_CFG = tmp32;
  [COLOR="#FF0000"]
  tmp32  = (ADC2_CFG & (0xFFFFFC00));
  tmp32 |= (ADC2_CFG & (0x03));  // ADICLK
  tmp32 |= (ADC2_CFG & (0xE0));  // ADIV & ADLPC

  tmp32 |= mode; 
  ADC2_CFG = tmp32;[/COLOR]
}

Currently analog.c only supports doing one analog operation at a time. So it only bothers to use ADC2 if you asked for one of the few pins that only have support on ADC2.

And likewise in "lite ADC" code I put in above sketch, there are a few pins that only are on ADC1... Which I marked (actually there are a couple of them I did not mark yet (that is most of the pins are in this table twice (one for the actual pin number and the other for the A number)...

I am not sure what else to do for a Light version... Could create another simple method like:
Code:
bool T4AnalogRead(uint8_t adc1_pin, uint16_t &adc1_val, uint8_t adc2_pin, uint16_t &adc2_val);

Obviously there is a ton of other stuff that one could try, like DMA support or other form of continuous updates, ...

But that probably is a very big diversion ;)
 
@KurtE

Yep it handles them but it setups up the same resolution and averaging for both channels (ADC1 and ADC2). At least I think that is how it is currently working. Changing it this way allows you to change them on a channel basis. Again haven't tested it yet.

KurtE said:
Obviously there is a ton of other stuff that one could try, like DMA support or other form of continuous updates, ...

But that probably is a very big diversion
Oh - big diversion! [EDIT] and I just went back to playing with usbhost :)
 
I checked and verified that analogReadAveraging(3) will speed things up. The result with analogRead() was 5 usec per sample.

With the right code, I'd expect < 1 usec per sample and 2x faster than that when alternating between two ADCs.

Perhaps others have hard data wrt "how fast is teensy 4 ADC?".

IMXRT1060RM_rev1_pdf__page_3_468_of_3_637_.jpg

Based on the manual, 0.85 us is possible. Also, analogReadAveraging(X), where X<4, should turn off hardware averaging, is that correct?

Code:
void analogReadAveraging(unsigned int num)
{
  uint32_t mode, mode1;
  
  //disable averaging, ADC1 and ADC2
  ADC1_GC &= ~0x20;
  mode = ADC1_CFG & ~0xC000;
  ADC2_GC &= ~0x20;
  mode1 = ADC2_CFG & ~0xC000;
  
    if (num >= 32) {
      mode |= ADC_CFG_AVGS(3);
      mode1 |= ADC_CFG_AVGS(3);

    } else if (num >= 16) {
      mode |= ADC_CFG_AVGS(2);
      mode1 |= ADC_CFG_AVGS(2);

    } else if (num >= 8) {
      mode |= ADC_CFG_AVGS(1);
      mode1 |= ADC_CFG_AVGS(1);

    } else if (num >= 4) {
      mode |= ADC_CFG_AVGS(0);
      mode1 |= ADC_CFG_AVGS(0);

    } else {
      mode |= 0;
      mode1 |= 0;
    }

  ADC1_CFG |= mode;
  ADC2_CFG |= mode1;
  
  if(num >= 4){
      ADC1_GC |= ADC_GC_AVGE;// turns on averaging
      ADC2_GC |= ADC_GC_AVGE;// turns on averaging
  }
}
 
Looks like there is some kind of bug. The first time it hits 4 the time is 36. The second time it is 279.


Quick and dirty T4 Analog Read stuff
1> 4 2 10 : 2 3 10 : 1 3 5 : 1 2 5
2> 1 2 10 : 2 0 10 : 0 1 5 : 1 0 5
4> 5 6 36 : 6 3 36 : 6 4 18 : 6 4 18
8> 7 6 71 : 7 4 71 : 6 4 35 : 6 4 36
16> 7 7 279 : 7 4 281 : 7 4 141 : 7 4 141
32> 7 7 279 : 7 4 281 : 7 4 141 : 7 4 141
64> 7 7 279 : 7 4 281 : 7 4 141 : 7 4 141
1> 1 2 10 : 2 0 10 : 1 2 5 : 1 1 5
2> 0 2 10 : 2 0 10 : 1 5 5 : 0 5 5
4> 7 7 279 : 7 4 281 : 7 4 141 : 7 4 141
8> 7 7 279 : 7 4 281 : 7 4 141 : 7 4 141
16> 7 7 279 : 7 4 280 : 7 4 142 : 7 4 141
32> 7 7 279 : 7 4 281 : 7 4 141 : 7 4 141
64> 7 7 279 : 7 4 281 : 7 4 141 : 7 4 141
 
Yes it is a bug in the analogReadAveraging

Code:
1(000006b7 00000000)> 7 507 10 : 19 507 10 : 1 507 5 : 2 507 5
2(000006b7 00000000)> 1 507 10 : 19 507 10 : 1 514 5 : 1 507 5
[COLOR="#FF0000"]4(000006b[/COLOR]7 00000020)> 8 515 36 : 12 515 36 : 8 517 18 : 8 513 19
8(000046b7 00000020)> 9 517 71 : 11 517 71 : 9 516 36 : 9 517 35
16(0000c6b7 00000020)> 10 517 280 : 10 517 281 : 9 517 141 : 9 517 142
32(0000c6b7 00000020)> 10 517 280 : 10 518 281 : 9 518 141 : 9 517 142
64(0000c6b7 00000020)> 9 518 280 : 10 517 281 : 9 518 141 : 9 517 142
1(0000c6b7 00000000)> 1 507 10 : 19 507 10 : 1 507 5 : 1 507 5
2(0000c6b7 00000000)> 1 507 10 : 19 507 10 : 1 513 5 : 1 507 5
[COLOR="#FF0000"]4(0000c6b7 [/COLOR]00000020)> 9 518 280 : 10 517 281 : 9 518 141 : 9 517 142
8(0000c6b7 00000020)> 10 517 280 : 10 517 281 : 9 518 141 : 9 517 141
16(0000c6b7 00000020)> 9 518 280 : 10 517 281 : 9 518 141 : 9 518 141
32(0000c6b7 00000020)> 10 517 280 : 10 517 281 : 9 517 141 : 10 518 141
64(0000c6b7 00000020)> 9 517 280 : 10 518 281 : 9 518 141 : 9 518 141

As you can see it is not properly updating the ADC1_CFG field (assume ADC2 as well)
Code:
  Serial.printf("%u(%08x %08x)> %d %d %u : %d %d %u : %d %d %u : %u %u %u\n",
                averaging_count,ADC1_CFG, ADC1_GC,
                a0, a1, t2 - t1,
                a0_4, a1_4, t3 - t2,
                a0_4C, a1_4C, t4 - t3,
                a0_both, a1_both, t5 - t4);

Looks like: void analogReadAveraging is buggy!

That is: look at how ADC1_CFG is updated.
Code:
void analogReadAveraging(unsigned int num)
{
  uint32_t mode, mode1;
  
  //disable averaging, ADC1 and ADC2
  ADC1_GC &= ~0x20;
[COLOR="#FF0000"]  mode = ADC1_CFG & ~0xC000;[/COLOR]
  ADC2_GC &= ~0x20;
  mode1 = ADC2_CFG & ~0xC000;
  
    if (num >= 32) {
      mode |= ADC_CFG_AVGS(3);
      mode1 |= ADC_CFG_AVGS(3);

    } else if (num >= 16) {
[COLOR="#FF0000"]      mode |= ADC_CFG_AVGS(2);[/COLOR]
      mode1 |= ADC_CFG_AVGS(2);

    } else if (num >= 8) {
[COLOR="#FF0000"]      mode |= ADC_CFG_AVGS(1);[/COLOR]
      mode1 |= ADC_CFG_AVGS(1);

    } else if (num >= 4) {
[COLOR="#FF0000"]      mode |= ADC_CFG_AVGS(0);[/COLOR]
      mode1 |= ADC_CFG_AVGS(0);

    } else {
      mode |= 0;
      mode1 |= 0;
    }

[COLOR="#FF0000"]  ADC1_CFG |= mode;[/COLOR]
  ADC2_CFG |= mode1;
  
  if(num >= 4){
      ADC1_GC |= ADC_GC_AVGE;// turns on averaging
      ADC2_GC |= ADC_GC_AVGE;// turns on averaging
  }
}
If you look at the RED lines. The variable mode is assigned the current value of the ADC1_CFG minus the bits associated with ADC_CFG_AVGS(n)... Which is fine, and then we update the mode variable to have the proper value for those bits, and then we or mode back into ADC1_CFG

First fix attempt:
Code:
  ADC1_CFG = mode;
  ADC2_CFG = mode1;

That appears to work, so I pushed the change up to github cores
@Paul - if you are looking it is in my currently pending PR in cores: https://github.com/PaulStoffregen/cores/pull/404

Also updated sketch version as well, and change sketch to use it...View attachment T4_ADC_Lite-191202c.zip
 
That appears to work, so I pushed the change up to github cores
@Paul - if you are looking it is in my currently pending PR in cores: https://github.com/PaulStoffregen/cores/pull/404

Also updated sketch version as well, and change sketch to use it...View attachment 18330

Thanks @KurtE. I'll give this a go. For analogRead() statements for two pins with 1 average and 10 bits of resolution, it takes about 10us to complete. Let's see if using separate ADCs makes any difference.
 
Status
Not open for further replies.
Back
Top