Teensy 3.6 ADC sampling error

Status
Not open for further replies.

Abadi alali

Active member
I am using the teensy 3.6 and I need to sample 2 signals at the same time to get the phase and amplitude of each, and I am having the following problems:

- There is a 1 micro sec delay in the sampling period, so for example if I set it to 1 micro which gives me 1 MHz sampling rate the ADC sometimes takes an extra micro sec to give the results which misses up my sampling rate. I tried making the sampling rate higher but I am still getting the same one mico sec error (note I am measuring the time using elapsedMicros)
- The synced mode is giving me a maximum sampling rate of 200 kHz, is that the limit?!
- what is the maximum sampling frequency for these ADC models I couldn't find that anywhere.

Thanks for your help.

Note: I am using the examples provided with the library and just adding the time previewing.
This is my code for the synced mode (this is the part in the loop function while the rest is left as it is in the example with setting bot ADCs to 16 bit on Very_hight_speed sampling and conversion:

for (int i=0;i<=1025;i++)
{

result = adc->analogSynchronizedRead(readPin, readPin2);
t=time;
value = (uint16_t)result.result_adc0;
value1 = (uint16_t)result.result_adc1;

}
for (int i=1;i<=1025;i++)
{

Serial.print(t, DEC);
Serial.print(" ");
Serial.print(value*3.3/adc->getMaxValue(ADC_0), DEC);
Serial.print(" ");
Serial.println(value1*3.3/adc->getMaxValue(ADC_1), DEC);
//delayMicroseconds(100);
}
}
}
 
Last edited:
A sampling rate of 1MHz is beyond the ADC specification, if I remember correctly. In your for() loop where you do the synchronized reads, you even don't care about timing at all, you let it just run....

The maximum sample rate specified by the manufacturer can for sure be found in the reference manual, just a moment...
 
Ok, looked it up. The conversion time an thus the sampling rate depends strongly on your settings of the ADC control registers for the ADC clock, the ADC conversion speed, precision, sample averaging, and power modes. Out of that, F_CPU and F_BUS come into play. These can not be found in your code snippet thus nobody can tell as long as you don't publish full code. As you can see in the K66 reference manual, chapter 39, a single ended 16bit conversion can in the worst case take up to more than 60 ADC clock cycles plus a few additional bus clock cycles. Thus, the maximum sample rate is in your hands by finding the best compromise between precision, power consumtion, and speed and by setting the ADC configuration registers in an appropriate way. Then, you have also to make sure that the converted data gets to its destination without unnecessary time loss. During high speed operations, the use of DMA with ring buffers is highly recommended.
 
Thanks for your reply, actually I am letting it run so I can see whats the maximum, and the 1 micro sec error is there all the time even if I had some delay inside the loop.

I even tried to run a simple test with the for loop just counting the time as follows:

for (int i=0;i<=1025;i++)

t=time;
delayMicroseconds(2);
}

then when I print the value in t I can see that it doesn't increment by 1 micro sec all the time sometimes it increment by 2, so is there any reason for this or is my teensy diffected. Also, in some cases there is a pattern like it increment by 1 micro sec for 3 times then it increment by 2 micro sec!!
 
The delayMicroseconds() function is not 100% reliable since it depends on the systick which might tick wrong in case of heavy use of interrupts or perhaps CPU load, too.
If you need really precise timings, make use of either one of the programmable interrupt timers (PIT) or, still better, the PDB which can trigger most integrated peripherals like the ADCs and the DACs directly with high precision, totally independent of the CPU.
 
This is the portion you asked about from my code:

void setup() {
adc->setAveraging(1); // set number of averages
adc->setResolution(16); // set bits of resolution
adc->setConversionSpeed(ADC_CONVERSION_SPEED:: HIGH_SPEED); // change the conversion speed
adc->setSamplingSpeed(ADC_SAMPLING_SPEED:: HIGH_SPEED); // change the sampling speed

adc->setAveraging(1, ADC_1); // set number of averages
adc->setResolution(16, ADC_1); // set bits of resolution
adc->setConversionSpeed(ADC_CONVERSION_SPEED::HIGH_SPEED, ADC_1); // change the conversion speed
adc->setSamplingSpeed(ADC_SAMPLING_SPEED:: HIGH_SPEED, ADC_1); // change the sampling speed
adc->startSynchronizedContinuous(readPin, readPin2);
}

void loop() {


if (Serial.available()) {
c = Serial.read();
if(c=='c') { // conversion active?
for (int i=0;i<=1025;i++)
{

result = adc->analogSynchronizedRead(readPin, readPin2);

value = (uint16_t)result.result_adc0;
value1 = (uint16_t)result.result_adc1;
delayMicroseconds(5);
t=time;
}
for (int i=1;i<=1025;i++)
{

//Serial.print("Value ADC0: ");
Serial.print(t, DEC);
Serial.print(" ");
Serial.print(value*3.3/adc->getMaxValue(ADC_0), DEC);
Serial.print(" ");
Serial.println(value1*3.3/adc->getMaxValue(ADC_1), DEC);
//delayMicroseconds(100);
}
}
}

if(adc->adc0->fail_flag) {
Serial.print("ADC0 error flags: 0x");
Serial.println(adc->adc0->fail_flag, HEX);
if(adc->adc0->fail_flag == ADC_ERROR_COMPARISON) {
adc->adc0->fail_flag &= ~ADC_ERROR_COMPARISON; // clear that error
Serial.println("Comparison error in ADC0");
}
}
#if ADC_NUM_ADCS>1
if(adc->adc1->fail_flag) {
Serial.print("ADC1 error flags: 0x");
Serial.println(adc->adc1->fail_flag, HEX);
if(adc->adc1->fail_flag == ADC_ERROR_COMPARISON) {
adc->adc1->fail_flag &= ~ADC_ERROR_COMPARISON; // clear that error
Serial.println("Comparison error in ADC1");
}
}
#endif

digitalWriteFast(LED_BUILTIN, !digitalReadFast(LED_BUILTIN));


//delay(100);
}

a sample of the results from the serial monitor is below and you can see that the time ellapsed is not increasing with a constant increment it is 10 and 11 alternating:

3831584 1.5697093010 1.9846326113
3831595 1.6078279018 1.7996795177
3831605 1.6083817482 1.7787319422
3831616 1.9813596010 1.8244037628
3831626 1.9706842899 1.8848294020
3831637 2.0954635143 1.7806454897
3831647 1.9352849722 1.9127260447
3831658 1.8117142916 1.9124742746
3831669 1.7710276842 1.9415290356
3831679 1.9888625145 1.7932342291
3831690 1.9147905111 1.7905149460
3831700 1.6752024889 1.9674112797
3831711 1.7337148190 1.7185076475
 
Okay that make sense, Thanks a lot.

Is there any reference for those since I haven't really used them before.

Thanks in advance :D
 
Everything is in the 2000p. K66 reference manual linked somewhere on the PJRC website.

A quicker approach could be having a look onto the source code of the adc and audio libraries. Both have DMA functionalities and use optionally the PDB to generate precise timings. The PITs are used by the IntervalTimer objects which can be found in the Teensy core files.
 
Status
Not open for further replies.
Back
Top