Speeding up ADC to <5us per conversion

Status
Not open for further replies.

gbathree

Active member
I am working on a project which sampling a measurement frequently - at the slowest we need to sample every 5us. I've tested the ADC using analogread() on the Teensy 3.0 and have found it takes 20 - 23us to read an analog signal, which is just a bit too slow for our application. I've read through previous posts and it seems that Paul recently updated analogread to make it faster, which is why it's as fast as it is now (thanks for that, btw!) - but it's still too slow for what we're trying to do. I also know (or think I know) that the ADC itself is quite fast - it's 818Ksps in 16 bit or 461Ksps in <13 bit (~1 - 2 microseconds) based on reading page 36 and 37 of the K20 manual (http://cache.freescale.com/files/32bit/doc/data_sheet/K20P64M50SF0.pdf).

So far, I've tried a few things:

I tried to comment out some not completely necessary parts of the analogread() command in the analog.c library in the teensy core (commented out the calibration and the "if pin X then Y" part):

int analogRead(uint8_t pin)
{
int result;
/*
if (pin >= 14) {
if (pin <= 23) {
pin -= 14; // 14-23 are A0-A9
} else if (pin >= 34 && pin <= 39) {
pin -= 24; // 34-37 are A10-A13, 38 is temp sensor, 39 is vref
} else {
return 0; // all others are invalid
}
}

//serial_print("analogRead");
//return 0;
if (calibrating) wait_for_cal();
//pin = 5; // PTD1/SE5b, pin 14, analog 0
*/
ADC0_SC1A = channel2sc1a[pin];
while ((ADC0_SC1A & ADC_SC1_COCO) == 0) {
yield();
// wait
//serial_print(".");
}
//serial_print("\n");
result = ADC0_RA >> analog_right_shift;
//serial_phex16(result >> 3);
//serial_print("\n");
return result;
};

These changes didn't help much (saved about 1us).

I'm currently trying to use the port register directly (as per this http://forum.pjrc.com/threads/17532...PORT-DDR-D-B-registers-vs-ARM-GPIO_PDIR-_PDOR thread), but it's a bit confusing a slow going.

So I have 2 basic questions:

1) What exactly is it that's taking that 20us? The ADC itself is only taking part of it so I know that something else is taking up the rest, but I can't figure out what it is. If someone understands the breakdown of that 20us I'd love to hear it.
2) How can I reduce that time? I found a lot of posts for the ATMega using a prescaler to increase the response of the ADC while decreasing the accuracy, but I haven't been able to figure out how to do this on the Teensy's ADC. Is the port register shifting the way to go, or something else?

Sorry if this is a noob question, I'm fairly new at this but learning fast. Thanks,

Greg
 
I don't know much about it, but it might be faster if you reduce the resolution. I doubt that it averages multiple rapidly taken ADC values by default but there is a setting for that.

//bit resolution
analogReadRes(10);

//not sure if it goes down to zero.
analogReadAveraging(0);
 
I also imagine it would be faster if you ADC sampled directly to DMA. cmason posted some code here:

http://forum.pjrc.com/threads/21619...-using-interrupt?p=27629&viewfull=1#post27629

But, if you were able to sample every 5us(200000Hz), that gives you at best ~500 clock cycles to do something else. It's hard to imagine that you could do anything with the data other than throw it away.
I would think that DMA and interrupts would be required, and maybe you could send it straight to an sdcard.

Maybe you could electrically connect 4 teensy3's and sync them up, and have them alternate taking samples, merging the data later. Sounds problematic but just wondering.
 
Update:

Definitely turning off the averaging made the biggest difference - that gets the speed down to 3us from the standard 20us for a single analog read! Also, my labmate Chris developed a neat little trick for actually identifying when the analogread() was pulling the sample: Pull one digital pin HIGH, and connect it to a cap. On the other side of the cap, connect the analog pin through a resistor. When the digital pin goes LOW, the cap discharges at a known rate (based on the size of the cap and resistor RC constant) which is either calculable or visible on the scope. If you do a digitalwrite --> LOW followed immediately by an analogRead, the analongRead result tells you exactly where on that cap discharge curve you are. That means, you can tell exactly how many seconds it takes in the analogRead to actually pull the signal (you have to factor in about a us for the digitalWrite call)! We used a .1uF cap and a 50k resistor. Pretty neat trick, Chris is a pretty smart fella. At 3us, I'm not sure how useful it is for me right now, but it seemed worth sharing

I found that changing the analogReadRes() did not speed up analogRead at all, surprisingly.

I'm not sure if interrupts will be required for our specifications, but I definitely need to figure out how to store sufficient data quickly if we're going to take this many samples... I think I'm going to try the SD card option next as you suggested linuxgeek.

I'm going to document all my results on a General Discussion post on this project here if you want to keep up with it further: http://forum.pjrc.com/threads/23508-Teensy-3-0-based-device-for-measuring-photosynthesis

Greg
 
There are some bits in the control registers of the ADC to deliberately slow the ADC sampling down (typically for precision with higher source impedance drives).
You might want to check that the default values of these are to your liking.

The higher the rate you run the ADC the more care you have to have in the analog circuitry feeding it
as you may just be converting more high frequency noise and not getting better data. If your goal is to save the spinlocked CPU cycles you will want to redo the ADC for interrupts
(from a timer to control the rate, perhaps). There is an advantage of the spinlock though. It means the Teensy isn't changing external pins during the adc conversion
which would lower accuracy.
 
There are some bits in the control registers of the ADC to deliberately slow the ADC sampling down (typically for precision with higher source impedance drives).
FWIW, there is some dense ADC reading starting on p. 600 of the hardware guide to this chip. Print it out, have a coffee, read it a couple of times. There are functionalities described there that are yet to be implemented in the Teensy environment, such as differential channels, etc. Per the manual, higher ADC clock-speeds are allowable at lower resolutions - but you need to take care re: how fast to make the ADC vs. the desired accuracy, source impedance, etc. No such thing as a free lunch.

Freescale even has a free windows program to help you determine the allowable max. speed for the ADC clock for a given resolution. Poke around their site, I can't access my copy at the moment thanks to Parallels having been deliberately disabled by its developers once you upgrade to OSX10.8 (upgrade, yada yada). You can really fine tune the response of the ADC (i.e. how many ADC clock cycles it uses to sample a signal, etc.) once you drop deep down into the ADC registers. But I'd counsel the OP to understand the limits of the ADC first before adjusting the ADC clock, sample time, resolution, etc.

For example, in my application, I am sampling for 31 time constants to achieve 16 bit happiness. So now you have to account for source impedance, MUX impedance (inside the ADC), the capacitance of the ADC (5-10pF) etc. to figure out how long you have to sample to get a usable result. TI published a nice paper re: how many time constants are needed for a given resolution.
 
Last edited:
Thanks Constantin and adrianfreed. I'll give that a read and see if I can figure it out. Ideally, in the long run, we'd like samples as fast with as accurate timing as possible. Basically, the faster and more accurate the timing the more interesting science we can do. For the moment to get us started, I did a simple for and while loop and set the average to 1 (as Paul suggested) - posts are all here: http://forum.pjrc.com/threads/23508-Teensy-3-0-based-device-for-measuring-photosynthesis?p=30502 .
 
Status
Not open for further replies.
Back
Top