Teensy 4: Global vs local variables speed of execution

Status
Not open for further replies.
Look at the ADC library included in TeensyDuino Installer: pedvide.github.io/ADC/docs/Teensy_4_html/namespace_a_d_c__settings.html
> there may be a better example with more tricks
> maybe 12 bits isn't needed - faster
> maybe averaging is required - slower

Is desired 200Ksps across all 8 pins - or each pin? Code below manages 704,208 combined single sample reads per second for the pins in the array list - 88K reads of 8 pins.

A quick hack to this sample "...\hardware\teensy\avr\libraries\ADC\examples\readAllPins\readAllPins.ino" is doing a 12 bit read with single sample ( no avg ) ::
Code:
A0: 0.34. A1: 0.00. A2: 0.62. A3: 0.94. A4: 0.99. A5: 1.08. A6: 1.12. A7: 1.06. 
	8 pins read 88026 times per second

Hacked code follows - adjusted for running on T_4.1 <#elif defined(ADC_TEENSY_4_1) // Teensy 4.1> - it reads all it can of 8 pins, then once per second reports value read and how many times all were similarly read in prior second::
Code:
/* Example for analogContinuousRead
   It measures continuously the voltage on pin A9,
   Write v and press enter on the serial console to get the value
   Write c and press enter on the serial console to check that the conversion is taking place,
   Write t to check if the voltage agrees with the comparison in the setup()
   Write s to stop the conversion, you can restart it writing r.
*/

#include <ADC.h>
#include <ADC_util.h>

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

#if defined(ADC_TEENSY_LC) // teensy LC
#define PINS 13
#define PINS_DIFF 2
uint8_t adc_pins[] = {A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12};
uint8_t adc_pins_diff[] = {A10, A11};

#elif defined(ADC_TEENSY_3_0) // teensy 3.0
#define PINS 14
#define PINS_DIFF 4
uint8_t adc_pins[] = {A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13};
uint8_t adc_pins_diff[] = {A10, A11, A12, A13};

#elif defined(ADC_TEENSY_3_1) || defined(ADC_TEENSY_3_2) // teensy 3.1/3.2
#define PINS 21
#define PINS_DIFF 4
uint8_t adc_pins[] = {A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13,
                      A14, A15, A16, A17, A18, A19, A20
                     };
uint8_t adc_pins_diff[] = {A10, A11, A12, A13};

#elif defined(ADC_TEENSY_3_5) // Teensy 3.5
#define PINS 27
#define PINS_DIFF 2
uint8_t adc_pins[] = {A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10,
                      A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24, A25, A26
                     };
uint8_t adc_pins_diff[] = {A10, A11};

#elif defined(ADC_TEENSY_3_6) // Teensy 3.6
#define PINS 25
#define PINS_DIFF 2
uint8_t adc_pins[] = {A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10,
                      A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24
                     };
uint8_t adc_pins_diff[] = {A10, A11};

#elif defined(ADC_TEENSY_4_0) // Teensy 4.0
#define PINS 14
#define DIG_PINS 10
#define PINS_DIFF 0
uint8_t adc_pins[] = {A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13};
uint8_t adc_pins_dig[] = {A0, A1, A2, A3, A4, A5, A6, A7, A8, A9};
uint8_t adc_pins_diff[] = {};

#elif defined(ADC_TEENSY_4_1) // Teensy 4.1
#define PINS 8
#define DIG_PINS 10
#define PINS_DIFF 0
uint8_t adc_pins[] = {A0, A1, A2, A3, A4, A5, A6, A7};
uint8_t adc_pins_dig[] = {A0, A1, A2, A3, A4, A5, A6, A7, A8, A9};
uint8_t adc_pins_diff[] = {};
#endif // defined

void setup()
{

  pinMode(LED_BUILTIN, OUTPUT);

  for (int i = 0; i < PINS; i++)
  {
    pinMode(adc_pins[i], INPUT);
  }

  Serial.begin(9600);

  ///// ADC0 ////
  adc->adc0->setAveraging(1);                                    // set number of averages
  adc->adc0->setResolution(12);                                   // set bits of resolution
  adc->adc0->setConversionSpeed(ADC_CONVERSION_SPEED::HIGH_SPEED); // change the conversion speed
  adc->adc0->setSamplingSpeed(ADC_SAMPLING_SPEED::HIGH_SPEED);     // change the sampling speed

  ////// ADC1 /////
#ifdef ADC_DUAL_ADCS
  adc->adc1->setAveraging(1);                                    // set number of averages
  adc->adc1->setResolution(12);                                   // set bits of resolution
  adc->adc1->setConversionSpeed(ADC_CONVERSION_SPEED::HIGH_SPEED); // change the conversion speed
  adc->adc1->setSamplingSpeed(ADC_SAMPLING_SPEED::HIGH_SPEED);     // change the sampling speed
#endif

  delay(500);
}

int value = 0;
int pin = 0;

uint32_t lC = 0;
uint32_t lShow = 0;
elapsedMillis lT = 0;

void loop()
{
  lC++;
  if ( lT >= 1000 ) {
    lShow = lC;
    lC = 0;
  }
  int i;
  for (i = 0; i < PINS; i++)
  {
    value = adc->analogRead(adc_pins[i]); // read a new value, will return ADC_ERROR_VALUE if the comparison is false.
    if ( lShow ) {
      Serial.print("A");
      Serial.print(i);
      Serial.print(": ");
      Serial.print(value * 3.3 / adc->adc0->getMaxValue(), 2);
      Serial.print(". ");
      if (i == 9)
      {
        Serial.println();
      }
      else if (i == 11)
      {
        Serial.print("\t");
      }
      else if (i == 13)
      {
        Serial.print("\t");
      }
      else if (i == 22)
      {
        Serial.println();
      }
    }
  }
  if ( lShow ) {
    Serial.printf("\n\t%u pins read %u times per second \n", i, lShow );
    lShow = 0;
    lT = 0;
  }
  // the actual parameters for the temperature sensor depend on the board type and
  // on the actual batch. The printed value is only an approximation
  //Serial.print("Temperature sensor (approx.): ");
  //value = adc->analogRead(ADC_INTERNAL_SOURCE::TEMP_SENSOR); // read a new value, will return ADC_ERROR_VALUE if the comparison is false.
  //Serial.print(": ");
  //float volts = value*3.3/adc->adc0->getMaxValue();
  //Serial.print(25-(volts-0.72)/1.7*1000, 2); // slope is 1.6 for T3.0
  //Serial.println(" C.");

  // Print errors, if any.
  if (adc->adc0->fail_flag != ADC_ERROR::CLEAR)
  {
    Serial.print("ADC0: ");
    Serial.println(getStringADCError(adc->adc0->fail_flag));
  }
#ifdef ADC_DUAL_ADCS
  if (adc->adc1->fail_flag != ADC_ERROR::CLEAR)
  {
    Serial.print("ADC1: ");
    Serial.println(getStringADCError(adc->adc1->fail_flag));
  }
#endif
  adc->resetError();
}

Wow thanks, I will look into that. I’ve seen the pedvide library but I haven’t tried to implement it yet. The examples in the library are not making much sense right now. I need to learn more. I’m just starting out with coding and I’ve learned the C syntax so far. Need to study more to understand the stuff in that library.

I’ve looked at the examples and I understand parts of it but I don’t know what parts of the code I need.

I need 200 ADC readings per ms in total.

So every sensor needs to do 20 readings per ms. Although more is always better as long as they are reasonably accurate. The reason more is better because I also read the time difference of arrival of peaks from the different sensors so faster reads give less error because they read in sequence.

I think 0-1023 is preferable but 0-512 would also work. (Is that 10 bit?).

I could also read all sensors periodically and store them in a multidimensional array. Then every row of the array would be a fraction of a ms so every reading would have a time signature by the position in the array. After the 2ms has passed I could evaluate all the readings.
 
I just doubled my ADC experience :) Not sure any other examples or methods provide for reading an array of ADC pins, seemed I saw this once - so stopped looking when I found it.

Here's a quick update to run on T_4.0 at 10 bit resolution and read 8 pins into an array - then the 9th element records the time in micros().
Code:
A0: 1.32. A1: 1.32. A2: 1.33. A3: 1.32. A4: 1.32. A5: 1.32. A6: 1.32. A7: 1.32. 
	8 pins read 98356 times per second at us=50300002
A0: 1.32. A1: 1.32. A2: 1.33. A3: 1.32. A4: 1.32. A5: 1.32. A6: 1.32. A7: 1.32. 
	8 pins read 98356 times per second at us=51300000

This code is all consuming of CPU time in main loop.

So after reading all code executed will be keeping the code from the next set of reads. If kept consistent - you could use up to 72% of the processor cycles each loop and get back to the next read - With a delayMicroseconds(28); in the loop() 8 pins read 26.1K times per second and 25K is the number desired.

Here is the code of that example with array read added and some other unused code removed for T_4.0:
Code:
#include <ADC.h>
#include <ADC_util.h>

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

#if defined(ADC_TEENSY_LC) // teensy LC
#define PINS 13
#define PINS_DIFF 2
uint8_t adc_pins[] = {A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12};
uint8_t adc_pins_diff[] = {A10, A11};

#elif defined(ADC_TEENSY_3_0) // teensy 3.0
#define PINS 14
#define PINS_DIFF 4
uint8_t adc_pins[] = {A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13};
uint8_t adc_pins_diff[] = {A10, A11, A12, A13};

#elif defined(ADC_TEENSY_3_1) || defined(ADC_TEENSY_3_2) // teensy 3.1/3.2
#define PINS 21
#define PINS_DIFF 4
uint8_t adc_pins[] = {A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13,
                      A14, A15, A16, A17, A18, A19, A20
                     };
uint8_t adc_pins_diff[] = {A10, A11, A12, A13};

#elif defined(ADC_TEENSY_3_5) // Teensy 3.5
#define PINS 27
#define PINS_DIFF 2
uint8_t adc_pins[] = {A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10,
                      A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24, A25, A26
                     };
uint8_t adc_pins_diff[] = {A10, A11};

#elif defined(ADC_TEENSY_3_6) // Teensy 3.6
#define PINS 25
#define PINS_DIFF 2
uint8_t adc_pins[] = {A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10,
                      A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24
                     };
uint8_t adc_pins_diff[] = {A10, A11};

#elif defined(ADC_TEENSY_4_0) // Teensy 4.0
#define PINS 8
#define DIG_PINS 10
#define PINS_DIFF 0
uint8_t adc_pins[] = {A0, A1, A2, A3, A4, A5, A6, A7};
uint8_t adc_pins_dig[] = {A0, A1, A2, A3, A4, A5, A6, A7, A8, A9};
uint8_t adc_pins_diff[] = {};

#elif defined(ADC_TEENSY_4_1) // Teensy 4.1
#define PINS 8
#define DIG_PINS 10
#define PINS_DIFF 0
uint8_t adc_pins[] = {A0, A1, A2, A3, A4, A5, A9, A10};
uint8_t adc_pins_dig[] = {A0, A1, A2, A3, A4, A5, A6, A7, A8, A9};
uint8_t adc_pins_diff[] = {};
#endif // defined

void setup()
{

  pinMode(LED_BUILTIN, OUTPUT);

  for (int i = 0; i < PINS; i++)
  {
    pinMode(adc_pins[i], INPUT);
  }

  Serial.begin(9600);

  ///// ADC0 ////
  adc->adc0->setAveraging(1);                                    // set number of averages
  adc->adc0->setResolution(10);                                   // set bits of resolution
  adc->adc0->setConversionSpeed(ADC_CONVERSION_SPEED::HIGH_SPEED); // change the conversion speed
  adc->adc0->setSamplingSpeed(ADC_SAMPLING_SPEED::HIGH_SPEED);     // change the sampling speed

  ////// ADC1 /////
#ifdef ADC_DUAL_ADCS
  adc->adc1->setAveraging(1);                                    // set number of averages
  adc->adc1->setResolution(10);                                   // set bits of resolution
  adc->adc1->setConversionSpeed(ADC_CONVERSION_SPEED::HIGH_SPEED); // change the conversion speed
  adc->adc1->setSamplingSpeed(ADC_SAMPLING_SPEED::HIGH_SPEED);     // change the sampling speed
#endif

  delay(500);
}

int value = 0;
int pin = 0;

uint32_t lC = 0;
uint32_t lShow = 0;
elapsedMillis lT = 0;

void loop()
{
  // delayMicroseconds(28); // Benchmark guess at the time per loop to maintain 25+K reads of 8 pins per second

  lC++;
  if ( lT >= 1000 ) {
    lShow = lC;
    lC = 0;
  }
[B]  uint32_t lastR[9];
  lastR[8] = micros();
[/B]  for (int i = 0; i < PINS; i++)
  {
    [B]lastR[i][/B] = adc->analogRead(adc_pins[i]); // read a new value, will return ADC_ERROR_VALUE if the comparison is false.
  }
  if ( lShow ) {
    for (int i = 0; i < PINS; i++)
    {
      Serial.print("A");
      Serial.print(i);
      Serial.print(": ");
      Serial.print([B]lastR[i][/B] * 3.3 / adc->adc0->getMaxValue(), 2);
      Serial.print(". ");
    }
    Serial.printf("\n\t%u pins read %u times per second at us=%lu\n", PINS, lShow, [B]lastR[8][/B] );
    lShow = 0;
    lT = 0;
  }

  // Print errors, if any.
  if (adc->adc0->fail_flag != ADC_ERROR::CLEAR)
  {
    Serial.print("ADC0: ");
    Serial.println(getStringADCError(adc->adc0->fail_flag));
  }
  adc->resetError();
}
 
Awesome man. Thanks for this.

Do I need the #define of the other teensies? Can't I just keep this part:

Code:
#elif defined(ADC_TEENSY_4_0) // Teensy 4.0
#define PINS 8
#define DIG_PINS 10
#define PINS_DIFF 0
uint8_t adc_pins[] = {A0, A1, A2, A3, A4, A5, A6, A7};
uint8_t adc_pins_dig[] = {A0, A1, A2, A3, A4, A5, A6, A7, A8, A9};
uint8_t adc_pins_diff[] = {};
 
I just doubled my ADC experience :) Not sure any other examples or methods provide for reading an array of ADC pins, seemed I saw this once - so stopped looking when I found it.

Here's a quick update to run on T_4.0 at 10 bit resolution and read 8 pins into an array - then the 9th element records the time in micros().
Code:
A0: 1.32. A1: 1.32. A2: 1.33. A3: 1.32. A4: 1.32. A5: 1.32. A6: 1.32. A7: 1.32. 
	8 pins read 98356 times per second at us=50300002
A0: 1.32. A1: 1.32. A2: 1.33. A3: 1.32. A4: 1.32. A5: 1.32. A6: 1.32. A7: 1.32. 
	8 pins read 98356 times per second at us=51300000

This code is all consuming of CPU time in main loop.

So after reading all code executed will be keeping the code from the next set of reads. If kept consistent - you could use up to 72% of the processor cycles each loop and get back to the next read - With a delayMicroseconds(28); in the loop() 8 pins read 26.1K times per second and 25K is the number desired.

Here is the code of that example with array read added and some other unused code removed for T_4.0:
Code:
#include <ADC.h>
#include <ADC_util.h>

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

#if defined(ADC_TEENSY_LC) // teensy LC
#define PINS 13
#define PINS_DIFF 2
uint8_t adc_pins[] = {A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12};
uint8_t adc_pins_diff[] = {A10, A11};

#elif defined(ADC_TEENSY_3_0) // teensy 3.0
#define PINS 14
#define PINS_DIFF 4
uint8_t adc_pins[] = {A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13};
uint8_t adc_pins_diff[] = {A10, A11, A12, A13};

#elif defined(ADC_TEENSY_3_1) || defined(ADC_TEENSY_3_2) // teensy 3.1/3.2
#define PINS 21
#define PINS_DIFF 4
uint8_t adc_pins[] = {A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13,
                      A14, A15, A16, A17, A18, A19, A20
                     };
uint8_t adc_pins_diff[] = {A10, A11, A12, A13};

#elif defined(ADC_TEENSY_3_5) // Teensy 3.5
#define PINS 27
#define PINS_DIFF 2
uint8_t adc_pins[] = {A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10,
                      A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24, A25, A26
                     };
uint8_t adc_pins_diff[] = {A10, A11};

#elif defined(ADC_TEENSY_3_6) // Teensy 3.6
#define PINS 25
#define PINS_DIFF 2
uint8_t adc_pins[] = {A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10,
                      A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24
                     };
uint8_t adc_pins_diff[] = {A10, A11};

#elif defined(ADC_TEENSY_4_0) // Teensy 4.0
#define PINS 8
#define DIG_PINS 10
#define PINS_DIFF 0
uint8_t adc_pins[] = {A0, A1, A2, A3, A4, A5, A6, A7};
uint8_t adc_pins_dig[] = {A0, A1, A2, A3, A4, A5, A6, A7, A8, A9};
uint8_t adc_pins_diff[] = {};

#elif defined(ADC_TEENSY_4_1) // Teensy 4.1
#define PINS 8
#define DIG_PINS 10
#define PINS_DIFF 0
uint8_t adc_pins[] = {A0, A1, A2, A3, A4, A5, A9, A10};
uint8_t adc_pins_dig[] = {A0, A1, A2, A3, A4, A5, A6, A7, A8, A9};
uint8_t adc_pins_diff[] = {};
#endif // defined

void setup()
{

  pinMode(LED_BUILTIN, OUTPUT);

  for (int i = 0; i < PINS; i++)
  {
    pinMode(adc_pins[i], INPUT);
  }

  Serial.begin(9600);

  ///// ADC0 ////
  adc->adc0->setAveraging(1);                                    // set number of averages
  adc->adc0->setResolution(10);                                   // set bits of resolution
  adc->adc0->setConversionSpeed(ADC_CONVERSION_SPEED::HIGH_SPEED); // change the conversion speed
  adc->adc0->setSamplingSpeed(ADC_SAMPLING_SPEED::HIGH_SPEED);     // change the sampling speed

  ////// ADC1 /////
#ifdef ADC_DUAL_ADCS
  adc->adc1->setAveraging(1);                                    // set number of averages
  adc->adc1->setResolution(10);                                   // set bits of resolution
  adc->adc1->setConversionSpeed(ADC_CONVERSION_SPEED::HIGH_SPEED); // change the conversion speed
  adc->adc1->setSamplingSpeed(ADC_SAMPLING_SPEED::HIGH_SPEED);     // change the sampling speed
#endif

  delay(500);
}

int value = 0;
int pin = 0;

uint32_t lC = 0;
uint32_t lShow = 0;
elapsedMillis lT = 0;

void loop()
{
  // delayMicroseconds(28); // Benchmark guess at the time per loop to maintain 25+K reads of 8 pins per second

  lC++;
  if ( lT >= 1000 ) {
    lShow = lC;
    lC = 0;
  }
[B]  uint32_t lastR[9];
  lastR[8] = micros();
[/B]  for (int i = 0; i < PINS; i++)
  {
    [B]lastR[i][/B] = adc->analogRead(adc_pins[i]); // read a new value, will return ADC_ERROR_VALUE if the comparison is false.
  }
  if ( lShow ) {
    for (int i = 0; i < PINS; i++)
    {
      Serial.print("A");
      Serial.print(i);
      Serial.print(": ");
      Serial.print([B]lastR[i][/B] * 3.3 / adc->adc0->getMaxValue(), 2);
      Serial.print(". ");
    }
    Serial.printf("\n\t%u pins read %u times per second at us=%lu\n", PINS, lShow, [B]lastR[8][/B] );
    lShow = 0;
    lT = 0;
  }

  // Print errors, if any.
  if (adc->adc0->fail_flag != ADC_ERROR::CLEAR)
  {
    Serial.print("ADC0: ");
    Serial.println(getStringADCError(adc->adc0->fail_flag));
  }
  adc->resetError();
}

When I try to run the code it gives me an error that PINS (at the top of void setup) was not declared in this scope.

I'm not seeing why exactly.
 
I just doubled my ADC experience :) Not sure any other examples or methods provide for reading an array of ADC pins, seemed I saw this once - so stopped looking when I found it.

Here's a quick update to run on T_4.0 at 10 bit resolution and read 8 pins into an array - then the 9th element records the time in micros().

This code is all consuming of CPU time in main loop.

So after reading all code executed will be keeping the code from the next set of reads. If kept consistent - you could use up to 72% of the processor cycles each loop and get back to the next read - With a delayMicroseconds(28); in the loop() 8 pins read 26.1K times per second and 25K is the number desired.

Here is the code of that example with array read added and some other unused code removed for T_4.0:

To clarify, are you saying that I could read all pins once, then the processor can go grab a cup of coffee for 28 microseconds and still be in time to read all pins again if the requirement is to read them 26k times?
 
Put this at the top: "#define ADC_TEENSY_4_0"

> I need 200 ADC readings per ms in total.

No problem, the T4 can read the ADC at more than 700 samples per ms. > 1200 if using both ADCs.
 
Put this at the top: "#define ADC_TEENSY_4_0"

> I need 200 ADC readings per ms in total.

No problem, the T4 can read the ADC at more than 700 samples per ms. > 1200 if using both ADCs.

Ah thanks.

Can I remove the other code? I don’t think I need the #ifdef teensy 2 etc. Only the 4 right?

I also need to add #define ADC_DUAL_ADCS right?
 
BTW

Why are there only 8 analog pins in this library? Can I change that?

As far as I can see there are 10 analog pins on the teensy 4 that are accessable from the sides.
 
> pinMode(adc_pins, INPUT);

AFAIK, this configures a pin for DIGITAL input - not what you want, so delete this section.
 
That code as noted started as the named installed generic sample, the #ifdef code is specific adjustment depending on what board is in use.

At build time the specific Teensy 'Board' will define the proper one. When using only a single one - like T_4.0 all the other code can be carefully removed. If it fails to compile after editing - then something needed was removed from the general case or other problem.Indeed more pins are available - it was indicated that 8 pins were needed so the original was edited and extra pins removed for timing.

Starting fresh from the sample would not be bad - but would remove the applied edits and instrumentation for measurement.

Once it seemed doing pinmode(input) for analog read was bad or superfluous - but this sample was written by the library author - taking them out may be harmless ideally.

On last post I noted the comment at the top referred to another example and I removed the comment - so this generic example was the base of multiple examples. There is no use of or need for digital pins or other stuff in this use case.

Comparing the last posted code to the first or the original some removal was done - that was done just for shared understanding to see it work and at what speeds. Further cleanup or alternatives is a good learning opportunity - proceed slowly and carefully if unsure and be prepared to undo changes when problems arise. Small changes made and undone on error should lead to understanding and a desired end.

If you get stuck - posting code might allow review and help.
 
That code as noted started as the named installed generic sample, the #ifdef code is specific adjustment depending on what board is in use.

At build time the specific Teensy 'Board' will define the proper one. When using only a single one - like T_4.0 all the other code can be carefully removed. If it fails to compile after editing - then something needed was removed from the general case or other problem.Indeed more pins are available - it was indicated that 8 pins were needed so the original was edited and extra pins removed for timing.

Starting fresh from the sample would not be bad - but would remove the applied edits and instrumentation for measurement.

Once it seemed doing pinmode(input) for analog read was bad or superfluous - but this sample was written by the library author - taking them out may be harmless ideally.

On last post I noted the comment at the top referred to another example and I removed the comment - so this generic example was the base of multiple examples. There is no use of or need for digital pins or other stuff in this use case.

Comparing the last posted code to the first or the original some removal was done - that was done just for shared understanding to see it work and at what speeds. Further cleanup or alternatives is a good learning opportunity - proceed slowly and carefully if unsure and be prepared to undo changes when problems arise. Small changes made and undone on error should lead to understanding and a desired end.

If you get stuck - posting code might allow review and help.

Understood, you have helped me enough to get me started with the ADC example. I think I can begin experimenting with removing unnecessary code as you say.

Thinking about what paul stoffegren said about 32 bit ints being preferrable. Should I replace the uint8_t with uint32_t in the pedvide example?

For instance this part:

Code:
#define PINS 10
uint8_t adc_pins[] = {A0, A1, A2, A3, A4, A5, A6, A7, A8, A9};

Also what are the: uint8_t adc_pins_diff[] = {};

Can I just remove that?
 
easy to test with provided timing with cyccnt. Seems they would be better as unint32_t at the cost of 24 bytes of memory.

But the majority of time will be spent in the adc reading - so saving one cycle perhaps getting the pin# when the wait is for some microseconds of time for the analog read is the kind of focus on premature optimization where the first focus is getting the code right.

At the cost of code space optimal time would perhaps be unrolling that for() loop and have a series of 8 reads with a constant number for the pin# to read and assignment of the returned value into the array.

so many tiny options and possibilities which may result in a % of a small % overhead reduction. But that is just a distraction from getting the code completed when it can be evaluated and adjusted later.

Given the CPU takes 30% of its time to just get the ADC readings returned 200,000 times per second - the real challenge will be 25,000 times a second evaluating each of those 8 values and responding in 'real time' without taking more than 28us each time delaying the next set of reads.

As a quick start it might be good to leave the 8 ADC reads in place - but write the code needed to just evaluate and act on a single drum surface continuously and see how long that takes and refine that. Then you can abuse that drum as fast as you can and confirm worst case can work. Then expand that to the array of piezos - knowing that that total number of impact changes can be captured - then distributed over the other 7 channels will just be efficiently cycles that number of beat detects across the array of items - and the drummer changing drum heads will in fact reduce the number of impacts seen versus a single head roll.
 
easy to test with provided timing with cyccnt. Seems they would be better as unint32_t at the cost of 24 bytes of memory.

But the majority of time will be spent in the adc reading - so saving one cycle perhaps getting the pin# when the wait is for some microseconds of time for the analog read is the kind of focus on premature optimization where the first focus is getting the code right.

At the cost of code space optimal time would perhaps be unrolling that for() loop and have a series of 8 reads with a constant number for the pin# to read and assignment of the returned value into the array.

so many tiny options and possibilities which may result in a % of a small % overhead reduction. But that is just a distraction from getting the code completed when it can be evaluated and adjusted later.

Given the CPU takes 30% of its time to just get the ADC readings returned 200,000 times per second - the real challenge will be 25,000 times a second evaluating each of those 8 values and responding in 'real time' without taking more than 28us each time delaying the next set of reads.

As a quick start it might be good to leave the 8 ADC reads in place - but write the code needed to just evaluate and act on a single drum surface continuously and see how long that takes and refine that. Then you can abuse that drum as fast as you can and confirm worst case can work. Then expand that to the array of piezos - knowing that that total number of impact changes can be captured - then distributed over the other 7 channels will just be efficiently cycles that number of beat detects across the array of items - and the drummer changing drum heads will in fact reduce the number of impacts seen versus a single head roll.

I already have working code with 2 sensors. This is a single drum with 10 sensors. My code is capturing the peaks and time arrival of peaks so I can compare arrival times later to do some cool stuff not possible with just one sensor. It's pretty robust and captures new peaks within just a few ms after a previous peak which is important for playability.

Next step is trying the faster ADC and see if it can work with all sensors.

The only calculation I'm doing is a float multiplication for every sensor once every 2ms that decays the threshold so it follows the previous peak. I multiply the previous peak with 0.9 or so to get the new threshold. Maybe that may slow it down because multiplication takes some time although the teensy does it very fast in hardware.

So once every 2 milliseconds I do 10 float multiplications. I'm not sure if it will be too much for the processor and slow the ADC readings and throw the time difference of arrival off. Right now I'm only doing the two multiplications.

One way to do it faster would be to store the calculations in an array and search for the value in stead of calculating.
 
If your goal is to read several ads pins as quick as possible, I believe there are some higher speed ways that we experimented with (mostly @mjs513), where you chain up ADC with ADCL XBAR... and I think we had some experiments ding it with DMA. Not sure if we had it running as fast as possible and/or using timers. Again don’t know if we have examples doing this? Mike?
 
A single-precision floating point multiply takes one instruction cycle. So once every 2 milliseconds, the 10 FP multiplications will take 17 nanoseconds.
Right now, it's a waste of your time to be trying to "do it faster" because "it" is MUCH faster than you realize. You'd be better off concentrating on getting the code working with the 10 sensors.

Pete
 
To clarify, are you saying that I could read all pins once, then the processor can go grab a cup of coffee for 28 microseconds and still be in time to read all pins again if the requirement is to read them 26k times?

Yes, posted code has that line commented out - uncomment it and it reads the 8 pins 26K times per second. Not sure where 8 came from on this end ... if it is 10 - add two more analogs to that array and update PINS value and that will take more time - giving less time to 'grab coffee' - - but that is the budget.
Code:
#define PINS 10
uint8_t adc_pins[] = {A0, A1, A2, A3, A4, A5, A9, A10, A11, A12};
If this is code for T_4.0 only any other #ifdef code can be removed. And any used digital or other code can be removed - some was removed if the original included example is referred to.

Mentioned now are alternate read methods that may happen in the background perhaps - there may be better ways - but then the readings will be in stale buffers not live when seen and would have to divine the sample time. May not be a problem - depending on use case.

Looked at reading touch pins - IIRC - it seemed the read was initiated - then a wait - not sure if that applies to ADC reads - if 10 reads could be started in parallel - and then while starting #10 then #1 would be closer to done. But the provided example seemed to provide a quick way to read desired pins and give 70% - maybe 60% with reads of 10 analogs - of the processors cycles for other work. Make that work - leave the timing tracking in and when it gets too slow - it needs to be made more efficient if desired - put a heat sink on and bump up to 800 MHz or something at that time.

Paul noted recently that when multiplying by 0.9 it should be written as 0.9f to keep the math from being done in double rather than faster float. Double is hardware handled - but still slower and a waste if single precision float is all that is needed.
 
A single-precision floating point multiply takes one instruction cycle. So once every 2 milliseconds, the 10 FP multiplications will take 17 nanoseconds.
Right now, it's a waste of your time to be trying to "do it faster" because "it" is MUCH faster than you realize. You'd be better off concentrating on getting the code working with the 10 sensors.

Pete
That's good to know. It seems I can do any stupid stuff and the teensy will not care because it does it so fast.
 
> pinMode(adc_pins, INPUT);

Paul doesn't do this and explicitly says not to. And I get very different ADC results when I do. So I think it's clear - don't do it.
 
Would this be a proper way to set up a sensor reading of 10 pins into an array using the ADC library?

I posted in the ADC library thread but no reply.

Code:
#include <ADC.h>
#include <ADC_util.h>



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



#define PINS 10
#define DIG_PINS 10
#define PINS_DIFF 0
uint8_t adc_pins[] = {A0, A1, A2, A3, A4, A5, A6, A7, A8, A9};
uint8_t adc_pins_dig[] = {A0, A1, A2, A3, A4, A5, A6, A7, A8, A9};
uint8_t adc_pins_diff[] = {};



void setup()
{

Serial.begin(9600);



    ///// ADC0 ////
    adc->adc0->setAveraging(1);                                      // set number of averages
    adc->adc0->setResolution(10);                                     // set bits of resolution
    adc->adc0->setConversionSpeed(ADC_CONVERSION_SPEED::HIGH_SPEED);  // change the conversion speed
    adc->adc0->setSamplingSpeed(ADC_SAMPLING_SPEED::HIGH_SPEED);      // change the sampling speed



    ////// ADC1 /////
    adc->adc1->setAveraging(1);                                     // set number of averages
    adc->adc1->setResolution(10);                                     // set bits of resolution
    adc->adc1->setConversionSpeed(ADC_CONVERSION_SPEED::HIGH_SPEED);  // change the conversion speed
    adc->adc1->setSamplingSpeed(ADC_SAMPLING_SPEED::HIGH_SPEED);      // change the sampling speed



    delay(500);

}

void loop()

{

uint32_t sensorValue[10];

for (int i = 0; i < PINS; i++)
  {
    sensorValue[i] = adc->analogRead(adc_pins[i]);
  }


}

Do I need to #define the DIG_PINS and PINS_DIFF? What are the pins diff?

Could I just write:
Code:
#define PINS 10
uint8_t adc_pins = {A0 ... A9};

Or would I mess something up in the library that I'm not seeing here?
 
Last edited:
const uint8_t adc_pins[10] = {A0, A1, A2, A3, A4, A5, A6, A7, A8, A9};

Ah, since the pins will not change you made them constant.

The original example code from the ADC library didn't have them as constants but i guess it's a good idea to make them constant.
 
It is great that you are trying out different things here, but again not really sure of what you are trying to accomplish? Or how much faster you believe this code is versus, simply using analogRead(pin) ?

Again if you are trying to speed it up, I would do timing around the sets of reads, and see what the timing is... My Self I would probably put something like:
digitalWriteFast(13, HIGH);
<do your analog Reads>
digitalWriteFast(13, LOW);
and watch the signal using Logic Analyzer...

Without Logic Analyzer, I would use the cyclecount... Or probably just put in an outer loop that does it N times and use something like elapsedMicros (or simply grab the micros() at the start of the test and at end of the test) and subtract the two and print them... Then you have something to work off of, and know how much you are speeding things up.

Again I would start off with simple base line for analogRead() versus using ADC library.

But as I mentioned your code is not really gaining much over simple analogRead as you are in lock step using the ADC library reading one thing at a time and holding your program up waiting for it to complete. So again you have 2 analog converters, but only one of them is active at a time...

I assuming you have looked through the main thread for the ADC library: https://forum.pjrc.com/threads/2553...nsy-4-3-x-and-LC?p=45376&viewfull=1#post45376

I am not sure how much of it is fully described, but if I remember correctly, you can do it something like:

Code:
uint32_t sensorValue[10];

// Note: I would probably have two different lists as
// not all ADC pins are on both ADCs...
for (int i = 0; i < PINS; i+=2)
  {
    // start reads on both ADCs
    adc->adc0->startSingleRead(adc_pins[i]);
    adc->adc1->startSingleRead(adc_pins[i+1]);

    // wait for both to complete
    while (!adc->adc0->isComplete() || adc->adc1->isComplete()) ; 

    // now get both results
    sensorValue[i] = adc->adc0->readSingle();
    sensorValue[i+1] = adc->adc1->readSingle();
  }
}
Again typed this in on the fly and has been awhile since I played with this. But this should more or less double your speed to reading 10 ADC pins.

As I mentioned earlier, we had some additional stuff we were playing with before we worked with @pedvide to integrate into ADC library where you could setup each of these ADC modules to read one pin after another... but I don't think we ever integrated the stuff... This requires working with several of the subsystems, that you can read about in the IMXRT pdf files.
Like ADC, ADC_ETC, XBAR Some of the basics of it were integrated in order to support using timers and DMA.

Hope that helps some...
 
It is great that you are trying out different things here, but again not really sure of what you are trying to accomplish? Or how much faster you believe this code is versus, simply using analogRead(pin) ?

Again I would start off with simple base line for analogRead() versus using ADC library.

But as I mentioned your code is not really gaining much over simple analogRead as you are in lock step using the ADC library reading one thing at a time and holding your program up waiting for it to complete. So again you have 2 analog converters, but only one of them is active at a time...

I assuming you have looked through the main thread for the ADC library: https://forum.pjrc.com/threads/2553...nsy-4-3-x-and-LC?p=45376&viewfull=1#post45376

I am not sure how much of it is fully described, but if I remember correctly, you can do it something like:

Code:
uint32_t sensorValue[10];

// Note: I would probably have two different lists as
// not all ADC pins are on both ADCs...
for (int i = 0; i < PINS; i+=2)
  {
    // start reads on both ADCs
    adc->adc0->startSingleRead(adc_pins[i]);
    adc->adc1->startSingleRead(adc_pins[i+1]);

    // wait for both to complete
    while (!adc->adc0->isComplete() || adc->adc1->isComplete()) ; 

    // now get both results
    sensorValue[i] = adc->adc0->readSingle();
    sensorValue[i+1] = adc->adc1->readSingle();
  }
}
Again typed this in on the fly and has been awhile since I played with this. But this should more or less double your speed to reading 10 ADC pins.

As I mentioned earlier, we had some additional stuff we were playing with before we worked with @pedvide to integrate into ADC library where you could setup each of these ADC modules to read one pin after another... but I don't think we ever integrated the stuff... This requires working with several of the subsystems, that you can read about in the IMXRT pdf files.
Like ADC, ADC_ETC, XBAR Some of the basics of it were integrated in order to support using timers and DMA.

Hope that helps some...

Again I'm just starting out with coding and I just don't know how most of this stuff is done. I know how to do an analog read and I've made the peak tracking stuff and it's working great with analog read and two sensors. I looked at the waveforms in the serial plotter and I think it's doing 25 reads per sensor with two sensors. I could be mistaken. I need it to do at least 25 reads per sensor per millisecond with 10 sensors. I was thinking reading all 10 sensors once and then evaluate the reading, do 10 more, evaluate again and so on. So don't have to execute code between all readings but between every 10 readings.

Maybe what you are saying is I don't need the pedvide library and can just use different settings of the analog read but I didn't know you could do that. It seems to me analogread is pretty slow but maybe I'm mistaken.

Your example makes sense, I thought the two ADC's did that automatically. In pedvides thread he mentions that the sensors will be read by the available ADC.

I will look through the Pedvide adc thread again since my understanding of all this has increased and maybe I will understand it more now.
 
Last edited:
@KurtE points to the more efficient - it seems - use of twin ADC's - as long as the pins are ordered so the ADC can see the pin given in hardware.
 
Status
Not open for further replies.
Back
Top