Improving ADC resolution

Showcase your projects! We love to see what people come up with, so please share your creations here.
qwerty
Posts: 18
Joined: Mon Oct 07, 2013 11:54 pm

Improving ADC resolution

Postby qwerty » Sun Oct 13, 2013 10:48 am

G'day to all you free tronics out there.

Arduino compatible boards such as the UNO/Freetronics11 have painfully low ADC resolution of 10 bits, meaning a full-scale count of 1023, while even the cheapest $8 DVM has 2000 counts. That's not near good enough for me, so I set myself the challenge of improving it.

I have successfully raised the resolution of a Freetronics 11 to an effective full scale count of around 50,000, which is rather useful for measuring small changes in DC levels or slowly changing signals. By "effective full scale count of 50,000", I mean that the reading is stable to within about plus-or-minus 1 part in 100,000, so the user can easily detect a change of 1 part in 50,000.

That represents the ability to detect a change of 100 uV (0.1mV) in 5V, about 50 times better than the nominal 4.9 mV resolution of the ADC. In my case, it gives me the abilty to detect changes in measured temperature of around 0.002 DegC using a thermistor, but I'm sure there must be more applications out there that could benefit. In my case the measurement of thermistor resistance is done ratiometrically, in which case the absolute calibration accuracy of the DAC is not important anyway, but the resolution sure is.

Note that potential improvements in absolute accuracy of the ADC voltage measurement are much more modest, you can gain maybe a factor of two to four, but that doesn't mean the vast improvement in resolution (the ability to measure very small changes) is not welcome. Similarly, DVMs invariably have significantly better resolution than absolute accuracy.

The other caveat, is that the method is only applicable to measuring DC levels, or signals that change slowly with time, which is also the case with digital voltmeters.

Cost is a few dollars for a few R and C, plus the use of one digital output pin.

To cover the topic thoroughly, this post shows ADC measurements of the standard Freetronics 11, to be later followed by details of the circuit, program, and the final "high-res" results.

The attached plot shows the DAC output (as read from the standard board), as a function of the input voltage, measured by a 6.5 digit DVM. As expected, we see a 'staircase', where the DAC output takes on integer values between 0 and 1023.

However, in this case the measurement is actually taken 500 times at a reading rate of 1 kHz, and then averaged, so the effective read rate is 2 Hz, similar to a DVM. The multiple readings and averaging reduce DAC noise but, as you can see, does not in itself improve the DAC resolution.

To be continued if there is interest in the topic.

PS to those interested in the finer details of how the data was measured. For my application, I have changed the ADC full-scale to 3.419V, by providing 3.419V on the Aref pin, and selecting using the analogReference command. To obtain the plotted data, a 10-turn potentiometer was used to divide down Vref and thus apply a variable voltage to an analog input. With this ratiometric arrangement, drift in the reference voltage does show up in the ADC measurement because Vref and Vin move up and down together. What the ADC output is actually measuring here, is Vin as a proportion of Vref. For example, if the ADC read 500 counts, that means that Vin is 500/1023 of Vref, regardless of the actual value of Vref. I set the measurement up that way partly because it simulates the way that my application measures thermistor resistance, and partly because I didn't want drift in Vref to affect the measuerment of ADC performance. That's great, but it does mean that I also needed to measure (with DVM) the input voltage to the DAC, not as an absolute voltage, but also as a proprortion of Vref. Fortunately my 6.5 digit DVM has the ability to do that - this DVM will accept 2 analog inputs, and very accurately display the ratio between them. Confused? Don't worry about it. The bottom line is that this method allows me to very accurately measure the DAC transfer function (DAC out vs. Vin) without requiring a stable DAC reference voltage.

PS2. I just noticed that on the plot is written "12-bit ADC", which of course should have said "10-bit ADC".
Attachments
ADC_transfer_plot.jpg
Last edited by qwerty on Sun Oct 13, 2013 10:21 pm, edited 2 times in total.

cef
Freetronics Staff
Freetronics Staff
Posts: 106
Joined: Wed Nov 09, 2011 12:53 am

Re: Improving ADC resolution

Postby cef » Sun Oct 13, 2013 9:31 pm

Very interesting. I'd definitely be interested in seeing more.

andrew
Freetronics Staff
Freetronics Staff
Posts: 978
Joined: Sun Jul 14, 2013 7:06 am
Location: Melbourne, Australia
Contact:

Re: Improving ADC resolution

Postby andrew » Mon Oct 14, 2013 2:21 am

Absolutely, please continue.

doc_pyro
Posts: 5
Joined: Mon Oct 14, 2013 9:53 am

Re: Improving ADC resolution

Postby doc_pyro » Mon Oct 14, 2013 9:59 am

I would be really interested have a thermistor problem this would solve for a bbq controller, well it would make me feel happier with better resolution.

qwerty
Posts: 18
Joined: Mon Oct 07, 2013 11:54 pm

Re: Improving ADC resolution

Postby qwerty » Mon Oct 14, 2013 12:33 pm

Sorry for the delay, been too busy taking measurements. This takes time and care if you want the measurement accurate to one part in 100,000, as necessary to characterize the very high resolution of the modified ADC.

So let's cut straight to the measured result, which is really very beautiful. Referring to the attached plot, the original 'staircase' is completely gone, replaced with a linear, continuous relationship between input voltage and ADC output, where 'ADC output' is the average of 500 readings as before.

The black line is the line of best fit to the purple measured points and, as you can see, the points fit almost exactly on the straight line, just as they should for an ADC exhibiting 'perfect' behavior. The average deviation from the straight line (at least over a small range in input voltage) is about plus-or-minus one part in 100,000.

I have used a method known as 'dithering' to improve the ADC resolution, which adds a small amount of 'noise' to the input voltage, which has the effect of 'smearing out' the 'staircase'. I used a triangular dither waveform of ~10mV P-P, obtained by analog integration of an AC coupled square wave generated on a digital output. While almost any small noise or AC waveform added to the ADC input will work to some extent, doing it optimally and in a very simple manner is less easy. This implementation requires no active components, just a few resistors and capacitors.

It may seem counter intuitive that adding noise can be beneficial, but the other side of the coin here is that you need to repeat the measurement many times and take the average, which removes the noise and leaves you with a vastly improved resolution. Even if you require 20 averaged measurements per second, you still get to average 50 samples at the maximum practical Arduino sample rate of ~1000 Hz. As I require only 2 measurements per second, I get to average 500 samples, removing the dither and the DAC noise as well, with the result that the averaged measurements fluctuate by only around plus-or-minus one part in 100,000.

Note that any non-linearity and offset error of the original ADC is not reduced by dithering or averaging. Fortunately the relatively large offset error of 1.7 counts for my UNO board seems very stable, so a one-off software correction works well. Drift in the measurement, usually related to changes in temperature, will not average out, but appears to also be very small. Last night at 19 DegC room temperature, the system was measuring 40.00, in units of ADC counts, in a full scale of 1023 counts. This morning at a chilly 14 DegC room temperature, it was reading 39.99, for an amazingly low overnight drift of 0.01 counts or, if you prefer, 1 part in a full scale of 102300. This remarkable stability is largely because of the ratiometric method I have employed where drifts in the ADC reference cancel out, but it does show that the underlying DAC is actually very stable. Frankly though, I should repeat that experiment at a higher measured value of say 500 counts, where I predict the result will still be very good, even if not quite as good as 1 in 100K. For those that require stable measurement of an absolute voltage, as opposed to a ratio of voltages, an external precision voltage source ($5 IC) could be used to provide the reference on the Aref pin. To put things in perspective, a quality metal film resistor has a temperature coefficient of 20 part-per-million per DegC. For a 5 degree temperature change (eg 19 to 14 DegC), the resistance will change by 5x20 = 100 ppm, which is 1 part in 10,000. So, a drift of 1 in 100,000 for a 5 DegC change is very small indeed.

Ironically, poor engineering that picks up a lot of noise on the input can easily have the same effect of improving the resolution, but is very hit-or-miss, and can equally well increase the noise (scatter) in the measurements. If you look carefully at the original 'staircase' data, you see that there is already a slight rounding of the sharp edges, caused by noise in the DAC.

Gotta go for now, but I will provide CRO traces of the dither waveform, circuits, code etc, plus further data showing what happens when the dither amplitude is not optimum.
Attachments
ADC_transfer_plot3.jpg

qwerty
Posts: 18
Joined: Mon Oct 07, 2013 11:54 pm

Re: Improving ADC resolution

Postby qwerty » Wed Oct 16, 2013 1:09 am

There doesn't seem to be that much interest in this stuff, but I'll press ahead anyway. Eventually I might post a summary on the Arduino forum.

The first attachment shows the triangular dither waveform, derived from a 50% duty cycle square wave generated as a PWM signal on one of the digital output pins. In the next posting I'll finally get around to attaching the simple circuit for this.

In Arduino, the PWM frequency is fixed at ~490Hz, which fortunately is quite suitable for dithering. It is essential that the dither frequency not be exactly the same as the ADC sampling frequency but, subject to that, the dither frequency is not important over a remarkably wide range. Of course, I tested this initially with a variable frequency triangle-wave generator (Agilent 33250A).

However, the amplitude of the dither signal needs to be roughly right. A simple rule is to make sure the amplitude of the triangular dither is somewhere between 2 and 4 times of one LSB of the ADC, and the dithering will work "pretty well". Usually the Arduino ADC measures from 0 to 5V, so 1 LSB is 5/1023 = 4.9mV. Therefore, just arrange for the dither to be somewhere between 10 and 20 mV peak-to-peak (P-P), and it will work just fine.

However, if you are a perfectionist like me, then look carefully at the attached plot, and observe the slight ripples in the transfer curve, where the dither amplitude was not optimized. For almost all practical purposes these small ripples won't matter, and it is very unlikely you will ever be aware of them, or be able to measure them. However, these ripples essentially disappear if you set the dither P-P amplitude to exactly twice of 1 LSB, which is 9.8 mV P-P for the default 5V full scale of the ADC. That is why the attached plot of dither waveform shows an amplitude of 9.8 mV P-P. The corresponding transfer curve was shown in the previous posting, where you observe a lovely straight line with no ripples. Ah, engineering, beautiful one day, perfect the next.

If you follow the simple soon-to-be-presented circuit then the dither waveform and amplitude will be correct and it will all work perfectly.
Attachments
ADC_transfer_plot4.jpg
dither_waveform.jpg

qwerty
Posts: 18
Joined: Mon Oct 07, 2013 11:54 pm

Re: Improving ADC resolution

Postby qwerty » Fri Oct 18, 2013 6:04 am

Finally, the simple circuit for dithering an analog input, thus increasing the resolution from a painfully low 1023 counts to an equivalent of 50,000 counts or more, depending on how many readings are taken and averaged. For my case of 500 averaged readings taken at a 1 kHz read rate, the update rate of averaged readings is 2 per second, about the same as most DVMs, and you will be able to easily resolve a voltage change of 1 part in 50,000 of full scale, with the averaged reading fluctuating by about plus-or-minus 1 part in 100,000.

The 1.0 uF capacitor should preferably be of the non-polarized type, to reduce leakage that might otherwise compromise DC accuracy. This capacitor, in conjunction with the 250 Kohm resistor, form a low-pass filter that converts the digital square wave to a triangle wave suitable for dithering - see CRO waveform in previous posting. For the very best performance, the 250K Ohms is easily realized with 220K and 33K in series, or just use 220K or 270K and it will still work just fine, with just a bit more ripple in the ADC transfer function. See previous posting.

The 0.01 uF capacitor provides AC coupling, guaranteeing that the addition of the dither produces no DC shift in the voltage reading. As the digital square wave is 0 to 5V unipolar, the triangular dither waveform would have a significant DC component if this capacitor was not present.

The 2.2 Kohm resistor isolates the dither from the source impedance of the voltage that is being measured. Ideally the voltage source being measured should have zero (or at least low) impedance, which would 'short out' the dither in the absence of the 2.2K resistor.

The code for generating the square wave and averaging the readings is trivially simple, but I will show it in a subsequent posting for completeness.

I have thoroughly tested the performance of this simple circuit for improving the ADC resolution using professional-grade equipment, and I am amazed at just how good the result is. Given the simplicity of the circuit, IMHO you would be mad not to upgrade the resolution in this way, in any application where you have 1/20th of a second or more available to take multiple readings and average. This is as close as it gets to a free lunch. Mmm, I like free lunches.

In my application of a dual-channel precision thermistor-based temperature readout, the original readout resolution of 0.1 Degrees (not good enough for my needs) has been improved such that I can now easily resolve changes of 0.002 DegC (2mK). Furthermore, to my surprise, the long term stability of the voltage (and thus temperature) readings is also outstanding, and I'll talk more about test results for that if anyone is interested.
Attachments
dither_circuit.jpg
Last edited by qwerty on Fri Oct 18, 2013 7:04 am, edited 3 times in total.

doc_pyro
Posts: 5
Joined: Mon Oct 14, 2013 9:53 am

Re: Improving ADC resolution

Postby doc_pyro » Fri Oct 18, 2013 6:16 am

Hi there are people interested like me for example please continue. It looks very interesting can't wait for the rest so i can build some thing...

qwerty
Posts: 18
Joined: Mon Oct 07, 2013 11:54 pm

Re: Improving ADC resolution

Postby qwerty » Fri Oct 18, 2013 8:07 am

doc_pyro wrote:Hi there are people interested like me for example please continue. It looks very interesting can't wait for the rest so i can build some thing...


Sorry, I originally forgot to attach the circuit to the previous email, but it is there now.

You are building a BBQ controller, so of course you need milliKelvin resolution to cook those sausages optimally. I completely understand.

This thread discusses improving ADC resolution rather than thermistors per se, but if you need some good general information about wiring thermistors to Arduino then take a look here: http://learn.adafruit.com/thermistor/using-a-thermistor

However, they really should have a capacitor across the thermistor or who knows how much and what type of noise might end up on the input, especially if unshielded thermistor leads are used.

If you use my dither circuit, then as a bonus the noise filtering is done for you very effectively by way of the 2.2K resistor and 1 uF capacitor.

qwerty
Posts: 18
Joined: Mon Oct 07, 2013 11:54 pm

Re: Improving ADC resolution

Postby qwerty » Fri Oct 18, 2013 11:34 am

So how stable is the voltage reading of the dithered ADC, over time and with changes in room temperature? It is certainly useful to be able to detect a change of say 1 part in 50,000, but it is a lot more useful still if the voltage readings are highly stable and reproducible over minutes, hours or days. Could I dare to hope that the (dithered) ADC in a 10-bit $4 micro could be stable to within 1 part in 50K, over time and temperature? I would not think so, would you?

To find out, I have set up the following experiment. I am not interested in measuring drift arising from the ADC reference, nor from resistors in any added circuitry, as these drifts are separate from the ADC, and can be eliminated if necessary.

The test circuit consists of a 2K5 plus 2K5 voltage divider between the 3.3V rail, and ground. The mid-point of the divider (at 1.65V) is read by the input of the "dithered" ADC, and the 3.3V rail is also used as the ADC reference, so drift in the 3.3V rail will not affect the ADC reading. Actually, this is the circuit that I use to measure temperature with a thermistor, with the thermistor temporarily replaced by a 2K5 resistor at the ground end of the divider.

Clearly the dithered+averaged ADC should read exactly 1/2 of full scale (1023/2=511.50), as the input is sitting at 1/2 of the reference. In practice the ADC has a DC offset error of 0.76 counts, which I correct for in the software, so the ADC does indeed read 511.50 counts, flutuating by about plus-or-minus 0.01 counts between adjacent readings. Actually I have 2 channels wired in this way, and they both read identically within 0.01 counts, not half bad in a full scale of 1023.0

OK, so with this circuit drift in the reference voltage won't show up in the ADC output, but drift in the 2K5 resistors sure will! However, in this case I have used extraordinarily stable resistors with a specified drift of 0.05 part-per-million per DegC, so we may regard these resistors as 'perfect' with effectively zero drift. For anyone interested, they are Vishay type Z201, and cost around $60 each, and I was just lucky to have a few that I could "borrow" from a job at work.

What this all means, is that the only source of drift that I will measure is drift in the ADC itself, which is exactly what I wanted.

At this stage, please forgive a short story. I have been in the electronics game for a long time, and in my younger and more naive days, would enthusiastically measure the drift of some circuit or another that I hoped would have ultra-low drift. So, I monitor the output with a 6.5 or 7.5 digit DVM, and over 3 or so hours measure essentially no drift at all, and erroneously conclude that the circuit is fantastic. Then, I come back and measure the next morning, when room temperature has dropped perhaps as much as 10 degrees, and find the circuit has drifted significantly, and the super-duper circuit is not as good as I at first thought. The problem is, that room temperature is often stable to within better than 1 degree for several hours, but temperature variations over a full 24 hour period are a different matter.

All this is to say, that meaningful testing of ADC stability takes days of monitoring, and/or the circuit must be deliberately placed in a fridge or oven to know whether it is really stable or not.

That said, so far the results look amazingly good. I have had the circuit operating all day, and the 2 channels have both read 511.50 plus-or-minus 0.01 counts for the entire day, from 9AM in the morning until 10PM as I write. The Arduino LCD display also displays the temperature calculated from the 2K5 resistors which have temporararily replaced the thermistors, and that displayed temperature is 22.643 DegC on both channels, and that reading has been stable all day to within plus-or-minus 0.001 DegC. But there is more. The circuit is run from a small battery, so with room temp at 23 DegC, I then placed the operating circuit in the fridge at 12 DegC. Incredibly, for this 11 degree change, the drift was on the order of 0.01 counts, or one part in 102300. To express in an industry-standard way, that is a thermal drift of around 1 part-per-million per degree (ppm/K), which is very, very, very, very ridiculously small.

Naturally, I will run the circuit overnight, and see what it reads in the morning.

To put the results gained so far into perspective, good-quality general purpose metal-film resistors specify a temperature coefficient of 20 ppm/K.

I'm not willing to hang my hat on this just yet, but the indications so far are that drift with time and temperature of the dithered ADC is effectively zero, and in practice the drift will be dominated by external resistors. I find this extraordinary.

I will keep you informed.


Return to “Project Showcase”

Who is online

Users browsing this forum: No registered users and 0 guests