Decreasing noise on Teensy ADC

Status
Not open for further replies.

gbathree

Active member
We are trying to chase down some noise on a pin photodiode detector we're using to measure IR fluorescence in plants. We recently made some hardware changes to the photodiode to dramatically reduce then noise entering into the system there, but now we've found that the remaining noise is coming from the Teensy ADC itself (we're running Teensy 3.0 right now).

The ADC noise level is about 40ish counts at 16 bits on Pin A11 (65536 counts total) or .07% on a single reading. I know that's pretty good in most applications, but the lower the better for us. We have what amounts to a sample and hold coming into the ADC, so we can take several (up to 20 or so) reads on the ADC and average them together so that helps. We also thought we could take 2 simultaneous reads using the Teensy 3.1, but I wasn't sure how to do that. Finally, we could get an external ADC but that would require more redesign.

So - any ideas on the cause of the noise, and suggestions for reducing it, and how does one access the 2nd ADC?

Greg
 
Have you looked at power supply noise? (For example, powered by USB from a computer). Solution would be a separate power supply.

Also, if the photodiode is high output impedance and/or there are long cables, that might be another area where noiuse is picked up. The solution would be a buffer to drive the cable from a low impedance; or two, drive two cables with opposite amplitude and use the ADC in differential mode to null out noise.

If light levels are low then you will get noise from the sensor itself (not a fault, the sensor is telling you the truth). Solution would be to pick up light from a larger area 9using optics, or multiple sensors) to reduce that noise.

Is the small percentage of noise statistically significant (are the results you are looking for buried in the noise)?
 
Last edited:
We have addressed power supply (we use a Lithium battery with very low internal impedance) - the photodiode is on a circuit board so there's no cables. Light levels are fairly high, but the signal itself is small (on top of the otherwise high light level)... we have simple optics in there now and could make more complex ones in the future, but not at the moment.

Definitely significant - we can see our signal, but there's components of it (like the slope at x = 100 below - ignore the bit spikes those are artifacts which we can get rid of) which are hard to identify, and we need to do multiple averages. We have this same setup in a different machine with a different ADC and the results are way way way better, so I'm pretty sure most of the problem is the ADC itself.

Selection_120.png
 
How much signal conditioning is between the photodiode sensor and the ADC input? Do you have any opamp gain stages, any filtering, etc?

The basic problem you'll have here is that the integrated ADC in Teensy 3.1 only has ~13 effective bits (ENOB - effective number of bits). This means that the combination of noise, non-linearities, etc degrade the performance to the point that only the upper 13 bits contain any real signal. The noise you're seeing is a little worse (40 counts of noise is a little worse than 11 ENOB), but the 13 bit spec in the datasheet only applies in the best conditions (no other IO, processor in wait or stop mode, etc), so it's not surprising to see that level of performance.

If you don't want to go with an external ADC, the best option is to add some analog signal conditioning before the ADC input to make better use of the available ADC range and its limitations. Your best bet is to add lots of filtering (noise power is proportional to bandwidth, so if you can reduce the bandwidth of the signal going into the ADC, you should be able to get better signal to noise), and add lots of gain (this may be a little tricky because it looks like you're trying to look at a small signal on top of a large offset). Thirdly, as Nantonos mentioned, use a differential signal if at all possible to reduce common mode noise (from power supply, etc).
 
Last edited:
Thanks whollender and pkourany - it sounds like the 12/13 bit actual resolution is somewhat of a hard limit mostly determined by noise within the chip itself. I was hoping that some new crazy ideas would emerge, or perhaps something had changed for 3.1, but not yet!

We have a pretty well designed filter and amp on the detector itself, and there are some other tricky things we can do to improve the signal, but they tend to come with drawbacks. As a result, we're looking at an external ADC for our next major revision, that seems to be the cleanest surefire way to improve signal with minimal negative consequences.

If you want to give any other suggestions, you can see the kicad files here: https://github.com/Photosynq/Photosynq-hardware .
 
Given the above, I agree that you are needing more bits of real resolution to see into the noise. An external ADC, preferably one running on a separate power supply from Teensy to avoid digital noise, looks like the obvious next step.

To quote myself from the thread referenced by pkourany:
For example, I have used an ADS1115 sitting on a separate PCB with it's own regulated power supply. That gives me 15 bits single-ended or 16 bits in differential mode (although with a max sampling rate of 860 sps). Using the 5V from a USB-powered Arduino, on the other hand, I got only 12 bits of useful information.
 
Light levels are fairly high, but the signal itself is small (on top of the otherwise high light level)

Definitely significant - we can see our signal, but there's components of it (like the slope at x = 100 below - ignore the bit spikes those are artifacts which we can get rid of) which are hard to identify, and we need to do multiple averages.
View attachment 1440

You have an ADC giving values between 0 and 65,535 and all the action in your measurements is between 140,000 and 142,000 or in other words 3% of the total range. So some of that bit depth is being wasted; you have around 11 bits (a range of 2048) effective.

Is it possible to measure with two sensors, one on an area with the pulse and one without? Then use differential mode so you are measuring "difference from control" (this will be a bipolar measurement). It means all the resolution is going where it matters.
 
I like Nantonos' suggestion of a differential measurement. The latest ADC library supports such measurements, AFAIK, and that might be just the ticket. FWIW, only one of the differential inputs is actually listed by Freescale as being 16 bit (see the manual), and then only if used differentially and likely with some heroic front end added on, extensive sleeping enabled throughout the rest of the chip to minimize noise, and so on. In other words, something that only happens in marketing land.

The remaining ADC inputs are 13 bit and below, again under ideal circumstances. The fact that Paul has tickled that sort of performance out of the chip using nothing more than the built-in ADC, voltage regulator, reference, etc. is very impressive all on its own. It speaks to the care he took routing those analog lines (which originate all around the chip), the analog ground plane, and the ferrites to isolate the analog power supply / ground plane. A simple shunt diode allows the teensy to enjoy all sorts of reference voltages from 1.7V up to 3.3V.

So, if possible, I'd have a go on a differential basis using two identical sensors, especially since your signal is unipolar. Hopefully, the sensors are relatively inexpensive!
 
Recently, I was testing the teesy 3 at 12bit resolution and found noise on the input even when it was grounded (supply noise was partially to blame)! It would seem to me that one possibility, instead of two sensors, is to use one ADC input as a noise reference and do the differential calculation on the unipolar input. This assumes that the noise on each ADC pin is similar if not identical (ie amplitude and phase). Now, this assumes that you have a spare ADC input and that it is ideally adjacent to the sensor ADC pin.

Even with differential input, any noise generated IN the teensy may make it through the internal comparator, especially if it is not in phase at each input. Noise sucks!
 
I took a quick look at the Kicad files that you linked to, and it looks like you may have some opportunities to reduce the noise of your signal without making major changes to your design so far (assuming I'm reading it correctly :)).

It looks like the circuitry around U105 is the main gain stage for your photodiode, and that your output is on pin 2 of K101. I'm assuming that this pin is routed to the ADC input on the teensy board. It looks like the rest of the board is mostly designed around driving some high power LEDs. On a side note, if you're using PWM or another switching scheme to drive the LEDs at a certain power level, you may want to investigate shielding the photodiode and adc input circuitry.

The first thing I noticed is that the gain stage (U105) is based around a high speed, 250MHz opamp(LMH6601), but you're feedback network (R112 and C114) has a 3dB rolloff at only ~600kHz. You'll probably have better luck here with an opamp that's designed for low noise operation, and with lower bandwidth.

Next, your low pass filter is setup for ~600kHz, but the 3.x can only sample up to ~300kS/s in 16 bit modes. That means that the nyquist freq for the absolute fastest sampling rate is less than 200kHz. Any frequencies or noise above that will be aliased back into your ADC samples, which will greatly increase the noise that you see. However fast you're sampling the ADC, your low pass filter should be filtering out anything higher than half the sample rate. This is as easy as swapping out C114 for a different value, or you can put additional filtering directly on the ADC input pin of the teensy.

The last suggestion I have is to put some smaller ceramic capacitors in parallel with the decoupling cap you have for U105 (C112). A 22uF cap is good for low frequency decoupling, but you'll get much better high frequency power supply rejection if you add a ~0.1uF cap in parallel (best if it's as close to the opamp power supply input as possible).

Hope that helps a little!
 
My first post here so i should introduce myself.
My name is Robert and i work with Greg (gbathree) on the photosynq project designing the electronics.

Now if you load the SCH from github you will see there is no PWM directly to the LED (since old teensy 3.0 now did not have ADC unlike 3.1 we used 6'th order filter with low F3 to thoroughly clean out voltage generated by PWM so we can use it in our actinic circuit (V/I circuit)) sice any noise would leach into our measurements and add to the fluorescence value creating phony baseline that would modulate with changing light intensity.
We do use Luxeon Z LED's powerful little buggers but poured grounds are separated with really nice low noise fast regulators for power so tested very little electrical noise leaches (you can always check for that if you cover your light sources&detectors and all you have left is electrical noise that you can chase down, but it is pretty clear bugger, we will turn on perfection mode later when deadline mode turns off (ever?).
Also maybe in the second iteration we might go with DAC and infinite sample&hold approach, to increase number of clean (presetting/slow) analog outs :cool:.

Also ADC is not picking up sine or any change in waveform (so Nyquist is kinda off here) , when it samples, it samples same values for averaging purposes 6uS apart as close as we could make it work about 20-30 samples and then it stops (pulse train pretty much constant value) then waits for milisecs to do it again absolutely no aliasing plus we do not do FFT related audio stuff that we would care, data looks as it should exepct of noise...

Regarding opamp, we do need really fast rise time (<us) and stable operation, for some of the experiments, this little bugger (no matter what you think of it) is really nice stable and perform very well (selected between bunch) so I'm not touching that!
Also moving F3 down will ruin some of the requirements i menationed above.
Plus we are moving to new type of detector (integrating detector) and the same opamp with it's low input bias current again performs great (more and more impressed with that little bugger and it's price/performance ratio)...
Also on the output there is high pass filter to filter out some of the actinic signal that would ruin our dynamic range (new detector will do better there filter is moved further in front to increase dynamic range even more, photodiode has 9 decades by itself no match for these fast noisy opamps)

Also caps around are MLCC's so their impedance is quite a match without doubling it like in the old days (Big C Elco+Small C foil) since these MLCC ceramics covers both pretty well if you check impedance curve in the datasheet, been happy with that approach quite number of years did not fail yet.

Teensy 16b ADC is very noisy even tested out of photosynq circuit, on silent Vref and smooth voltage on input from lab power supply plus filter cap on it...
If i plot it looks like 800pp counts, i'm going to investigate that little bit more but it is pretty consistent (and worse) with what others did notice.
will attach plots of measurements done in clean test jig to start nibbling on that one.
 
Hi Robert,

No worries. If you need high speed, accurate time domain measurements, the ADC on the teensy probably isn't the best option.

I come from more of an RF background, so I tend to think about things in terms of frequency response instead of time domain response. I still think you need to worry about the aliasing, even if it's just for the higher frequency noise.

I'm a little confused about your requirement to have very fast rise times on the opamp, but only do a burst of samples on the order of milliseconds.

Anyway, it sounds like your main problem at this point is the ADC, and it sounds like you're already on the right track there.

Good luck!

--William
 
Hi Robert,

Thanks for the additional background. I presume you meant that the Teensy 3.0 doesn't have a PGA built into the ADC unlike the 3.1? That part has me a bit confused.

Anyhow, seems like you know what to do best, look forward to hearing how it all turned out!
 
I did some bare tests on Teensy 3.0 16b ADC read, hooked Teensy A10 input and Vref to lab bench PSU filter out little bit and this is how plot looks like:
teensy_noise.gif
I'm more concerned about these spikes random high count value from mean, ugh, there is some sign of 60Hz (wire loops but that can be taken care of)...

We have 20-150microsec measurement pulse, we sample at the top of the pulse (flat top typically) as fast as ADC will allow, that is every 6microsec, from few up to 30 samples, it varies how you set it up.
After that pulse is done, there is 1ms/1000us pause until next measurement pulse...
That is one way of doing it it can be more intense on the RF (your) side (FRR).
Since these pulses are square'ish harmonics spread way higher...
 

Attachments

  • teensy noise.jpg
    teensy noise.jpg
    74.5 KB · Views: 693
I wouldn't trust the results of reading the lab power supply. They're notoriously noisy, even with lots of filtering.

You'll get a better idea of the intrinsic noise of the ADC by connecting the analog input to the middle of a resistor divider between AREF and AGND. The resistors will add some noise, but it should be way cleaner than the bench supply. Someone's mentioned somewhere that one of the ADC channels reads the internal reference, and that may give you a good idea of it as well.
 
it sounds like the 12/13 bit actual resolution is somewhat of a hard limit mostly determined by noise within the chip itself.

Yes indeed.

When I started Teensy 3.0, I was concerned people would read Freescale's marketing materials for these chips and believe the ADC was really capable of 16 bits. In their reference manual, "fact sheet", brochures, on their website and pretty much everywhere else, they call it a "16 bit" ADC. Only deep within the datasheet do they document the effective number of bits is really 13 with averaging, and less on single samples.

From the beginning on Kicktstarter and everywhere I've mentioned the ADC, I've tried to be clear the real resolution is at most 13 usable bits.

I was hoping that some new crazy ideas would emerge, or perhaps something had changed for 3.1, but not yet!

One thing you can do, which might be more trouble than simply adding an external ADC chip, is a fully differential signal on the analog only pins capable of differential input. The analogRead() function doesn't support this, so you'll have to hack the code a bit, but it's pretty simple to read the differential channel by just writing slightly different numbers to the registers.

But getting a fully differential signal from a source that isn't already such a signal, and doing so without adding noise that negates the benefit, it's trivial. Then again, if you're already using low noise opamps, maybe it's possible?

Of course, before you apply opamps to making a differential signal, you'd want to make sure you're using opamps to maximize your signals usage of the available ADC range. That graph on reply #3 seems to show a trend in the 141500 range and other in the 140500 range, or just a 0.8% change relative to the absolute scale. A pretty common approach involves subtracting out an offset with an opamp, then applying gain with another, so the signal uses the ADC's range more optimally.

However, depending on your analog circuit, the differential inputs might be really helpful? It's impossible to say, since I can't see your circuitry. But if that small signal on a huge offset is due to something that's a physical voltage, then perhaps you can feed the signal into A10 and the related voltage into A11, and measure it as differential. If the differential signal is always small, the PGA in Teensy 3.1 might be very helpful.


Small technical details often matter, but especially so with analog circuit design. If you're willing to show your circuitry, I might be able to offer better advice...
 
Awhile back I was trying to get cleaner samples. I'm no expert, but I believe the internal reference helped some. You are likely already doing that though. The next thing I was going to try, but did not, was to use a separate voltage reference chip. Early on, I thought someone on the forum (maybe elsewhere?) showed some very promising samples that claimed near 16-bit resolution with the T3. Also, someone on the forum was using the differential inputs with DMA, and was hoping to turn into a library, but never did AFAIK. But I think they do have their code somewhere on the site, or linked to it.

Here is what I remember using:
analogReadResolution(16);
analogAveraging(1);
analogReference(INTERNAL);
 
T3.0 ADC measurements - noise performance

The first thing I did when I got my Teensy 3.0 was test the ADC. This was before Paul had created this forum, so I posted my results in another forum: http://dangerousprototypes.com/forum/viewtopic.php?f=2&t=4606

I believe the version of the Teensy code I was using had some amount of internal signal averaging on the ADC set by default, before I got the value, which is why I saw better than 13 bits at least for RMS noise. RMS is normally what I use to compare things, because peak-peak noise is not a statistically robust measurement.

Teensy 3.0 arrived - Post by jbeale » Thu Oct 04, 2012 8:43 pm

My first test was to see how good the on-board ADC is. It is 16 bits, but Paul had said due to noise, it is effectively only 13 bits. You can see my experimental setup in this photo. ADC0 is driven by a 499/1.0k resistive divider from Vdd to AGND (analog ground pin), and a 0.1 uF cap from ADC0 to AGND. In this case Vref = Vdd = 3.266 V and ADC0 input is 2.171 V (per Fluke 179). This gives an expected reading of 65535*(2.171/3.266) = 43563 counts, where 1 LSB = 49.84 uV. Running the below code, with a sample size of 10000 readings, I get a standard deviation of just about 1 LSB (so RMS noise = 50 uV) and peak-peak noise of 7 counts, and a DC offset from the expected reading of 10-20 LSBs (= 0.5 to 1 mV).

The noise and offset from the expected reading depends somewhat on the clock rate set. Without that 0.1 uF cap from ADC0 to AGND, both p-p and std.dev noise increase about 3.5 times. The board is being powered by (no doubt noisy) +5.18V from a USB hub on a PC monitor, the 3.266 V is from the on-board regulator. The AGND pin is sensitive with this simple unshielded setup: even just connecting the ground lead of my battery-powered Fluke 179 multimeter and nothing else (and with the meter powered off) increases RMS noise from 0.9 to 1.6 LSB.
 
JBeale - we definitely read that through before we started, that was really helpful. We were hoping perhaps there were other workarounds.

stevech - Not really. If it was true 16 bit, our signal would look great even with only 2000 points of resolution. We are considering addressing this by expanding the range dynamically for each measurement - setting the comparator for the op amp based on the first point in the measurement (I think I've got my lingo right here, Robert correct me if I'm wrong please). We have to do it dynamically because one measurement could be 30,000 - 32,000 while the next is 2000 - 4000. That would increase the resolution, but it may create other problems and make the device harder to develop on.
 
Typical input signal prep for ADC includes amplifying signal so max value will correspond with max input voltage to the ADC (=Vref), but the problem here is that baseline changes and it can be from 0-Vref and signal is typically only few hundreds/thousand reads modulated into baseline!
So one not so usual way is to introduce automate dynamic (means it is not always the sam)baseline subtraction before ADC to utilize full dynamic range of the ADC excluding the baseline.
If you make one read beforehand just to determine the baseline value, then take that value to calculate subtract value that you generate with DAC, that analog value will be used to subtract from next read to center the signal and amplify that 100x (so this includes dual ADC or pga with MUX, few more opamps and passives adds complexity and cost...) that will increase s/n of a reading significantly, but sometimes you cannot do that (we can) and you're stuck with your baseline that has to be digitized and you rely onto ADC resolution so you can resolve your signal nicely, and that signal can be under 1% of your baseline i.e. in case of really low absorption in spectroscopy.
In case of Teensy 3.0/1 ADC is not that good, and signal prep is not subject here, i know how to do that in a various ways...
Basic question is why is ADC so crappy, and what to do to make it better, that same ADC inside teensy.
Yes we already ordered 16bit external ADC chips (fyi these chips even the cheapest ones are more expensive then ARM used in teensy so you tell me how much sense is in that solution.)
Basically teensy as is is best solution offered here, for what is does, just speculating to make it better with minimum investment, this is open source project so it is kinda a price sensitive!
 
As I reported, I was seeing near 1 LSB noise (RMS) using the default ADC setting in that early Teensy code release which I vaguely recall defaulted to 16x internal sample averaging per reported ADC value. But I only got that RMS noise result with the specific setup I described with the capacitor connected as I mentioned, and not touching the ground lead with my multimeter (!). Getting analog signals clean at 16 bits and up is increasingly non-trivial as you go up in frequency; attention to layout and ground currents etc. is required.

As Paul has mentioned, the manufacturer only claims 13 bit resolution for this device so if you do any better, count yourself lucky :). Many CPUs offer the option to halt the processor during an ADC conversion which can reduce internally generated noise but I don't know what the procedure to do that would be for Teensy 3.

As for "why is ADC so crappy" I see it the opposite way; many CPUs offer only 10 or 12 bit ADCs. True 16-bit ADC performance at speed is not easily done with all the signal interference inside a general-purpose CPU core. Freescale probably created unrealistic expectations when advertising the ADC register size, instead of being upfront with the measured performance.
 
Last edited:
Status
Not open for further replies.
Back
Top