Maximizing ADC readout Teensy 4

Status
Not open for further replies.
I am trying to maximize the ADC performance in my application on a Teensy 4.0. It is not going to be much more complex than the attached code. To test this I'm using another Teensy to send a pulse 50us wide every 2 seconds, I verified with a oscilloscope this is 50.54us wide. I am able to measure ~17 high ADC values with this code, suggesting a 0.34 MHz ADC rate. This seems a little bit slower than I would have expected, but I also cannot find specs on the ADC in the teensy 4.0.

If there is a way to get >0.5MHz or more out of the ADC I would be pretty happy. My fiddling around suggests the bottle neck is the ADC waiting at the isComplete() step. If I change the while(!isComplete()) to a if/else to count the number of times loop is run between isComplete, it's 18 loops.

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

const int readPin = A9; // ADC0

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

uint32_t currTime = 0;
uint32_t sample_count = 0;
uint16_t val = 0;
const int nsamples = 2000;
CircularBuffer<uint16_t, nsamples> wf_buffer;
CircularBuffer<uint16_t, nsamples> ts_buffer;
const int pre_sample = 50;
const int post_sample = (nsamples - pre_sample);
int trigger = 0; // 0 if not triggered, otherwise samples since trigger
const int threshold = 500;
uint32_t num_triggers = 0;
uint32_t checksum = 0;
uint32_t uptime;


void setup() {
  Serial.begin(250000);
  adc->adc0->setReference(ADC_REFERENCE::REF_3V3);
  adc->adc0->setResolution(12);
  adc->adc0->setConversionSpeed(ADC_CONVERSION_SPEED::VERY_HIGH_SPEED);
  adc->adc0->setSamplingSpeed(ADC_SAMPLING_SPEED::VERY_HIGH_SPEED);
  adc->adc0->startContinuous(readPin);
  Serial.println("Begin...");
}

void loop() {
  currTime = micros();
  
  while(!adc->adc0->isComplete());
  val = adc->adc0->analogReadContinuous();
  wf_buffer.push(val);
  ts_buffer.push(currTime);
  if ((val > threshold) & (trigger == 0)) {
    trigger += 1;
  }
  else if (trigger == post_sample) {
    // write out the WF to the serial
    sendData();
    // reset trigger
    trigger = 0;
  }
  else if (trigger > 0) {
    // increment trigger until we get to the desired length
    trigger += 1;
  }
}

void sendData()
{
  Serial.print("Waveform Start ");
  Serial.println(millis());
  checksum = 0;
  for (int i = 0; i < 1000; i++) {
    checksum += wf_buffer[i];
    Serial.print(ts_buffer[i]);
    Serial.print(",");
    Serial.println(wf_buffer[i]);
  }
  Serial.print("Checksum ");
  Serial.println(checksum);
  Serial.print("Waveform End ");
  Serial.println(millis());
  Serial.println("ZZZ");
  delay(10);
}
 
Adjust the : adc->adc0->setAveraging(0); // set number of averages

It will sit longer taking multiple readings to average if that is higher - not sure of the default

>> * \param num can be 0, 4, 8, 16 or 32.
 
Adding adc->adc0->setAveraging(0); has no effect, I assumed it was the default, but it is now in my code, it is good to be explicit! Thanks
 
I wanted to change resolution to see if that was going to effect things. In doing so I se that these lines are needed, although I do not understand them (and expecially the comments)
Code:
  adc->adc0->enableCompare(1.0/3.3*adc->adc0->getMaxValue(), 0); // measurement will be ready if value < 1.0V
  adc->adc0->enableCompareRange(1.0*adc->adc0->getMaxValue()/3.3, 2.0*adc->adc0->getMaxValue()/3.3, 0, 1); // ready if value lies out of [1.0,2.0] V
After adding them I see now >1MHz for all resolutions I've tried.

Even more mysterious, reverting now to my old code version I still get >1MHz. Any idea what happened?

The current code in full
Code:
#include <ADC.h>
#include <ADC_util.h>
#include <CircularBuffer.h>

const int readPin = A9; // ADC0

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

uint32_t currTime = 0;
uint32_t sample_count = 0;
uint16_t val = 0;
const int nsamples = 2000;
CircularBuffer<uint16_t, nsamples> wf_buffer;
CircularBuffer<uint16_t, nsamples> ts_buffer;
const int pre_sample = 50;
const int post_sample = (nsamples - pre_sample);
int trigger = 0; // 0 if not triggered, otherwise samples since trigger
const int threshold = 500;
uint32_t num_triggers = 0;
uint32_t checksum = 0;
uint32_t uptime;
uint32_t loops = 0;


void setup() {
  Serial.begin(250000);
  adc->adc0->setReference(ADC_REFERENCE::REF_3V3);
  adc->adc0->setResolution(12);
  adc->adc0->enableCompare(1.0/3.3*adc->adc0->getMaxValue(), 0); // measurement will be ready if value < 1.0V
  adc->adc0->enableCompareRange(1.0*adc->adc0->getMaxValue()/3.3, 2.0*adc->adc0->getMaxValue()/3.3, 0, 1); // ready if value lies out of [1.0,2.0] V
  adc->adc0->setAveraging(0);
  adc->adc0->setConversionSpeed(ADC_CONVERSION_SPEED::VERY_HIGH_SPEED);
  adc->adc0->setSamplingSpeed(ADC_SAMPLING_SPEED::VERY_HIGH_SPEED);
  adc->adc0->startContinuous(readPin);
  Serial.println("Begin...");
}

void loop() {
  currTime = micros();

  if (adc->adc0->isComplete()) {
    //while(!adc->adc0->isComplete());
    val = adc->adc0->analogReadContinuous();
    wf_buffer.push(val);
    ts_buffer.push(currTime);
    if ((val > threshold) & (trigger == 0)) {
      trigger += 1;
    }
    else if (trigger == post_sample) {
      // write out the WF to the serial
      sendData();
      // reset trigger
      trigger = 0;
    }
    else if (trigger > 0) {
      // increment trigger until we get to the desired length
      trigger += 1;
    }
    loops = 0;
  }
  else {
    loops+=1;
  }
}

void sendData()
{
  Serial.print("Waveform Start ");
  Serial.println(millis());
  checksum = 0;
  for (int i = 0; i < 1000; i++) {
    checksum += wf_buffer[i];
    Serial.print(ts_buffer[i]);
    Serial.print(",");
    Serial.println(wf_buffer[i]);
  }
  Serial.print("Checksum ");
  Serial.println(checksum);
  Serial.print("Waveform End ");
  Serial.println(millis());
  Serial.println("ZZZ");
  Serial.print("Loops/conversion=");
  Serial.println(loops);
  delay(10);
}
 
Status
Not open for further replies.
Back
Top