Getting the best ADC performance from mbed

When using an ADC, for best results, it is important to consider ways to reduce noise. Here are the main things you can to to ensure noise is kept to a minimum when using the mbed Microcontroller :

  1. Unused ADC pins are either tied to ground, or declared as DigitalOut
  2. Quality of signal source, including low noise design techniques such as filtering.

The most marked influence is the state of the unused ADC pins. Grounding them, or configuring them as DigitalOut reshapes the profile of the noise by dramatically reducing the total number of large spikes. The low amplitude noise is most often due to the quality and integrity of the signal.

To ensure the quality and integrity of the original signal, standard circuit design techniques should be applied, such as filtering, termination, careful PCB routing (guard rails, ground planes, separated analog and digital planes, separation of high speed digital signals). There is no one single answer to this issue, it requires the application of best practices.

Background and Experiments

Lots of mbed users have shared their experience around noise in ADC readings based on various setups, including in particular situations where large spikes in ADC readings can be seen. This can be the case even when setting AnalogIn in to a fixed voltage and seeing the variations. The remainder of this page discusses some of these experiences, and some of the analysis that demonstrates the behaviour of applying the above recommendations.

Some of the fixes put forward so far include :

  • Software filtering techniques to remove the spikes
  • Decoupling capacitors to remove the electrical noise
  • The addition of a hand crafted ground plane by davemalham

It is possibly worth taking 5 minutes to read some of the history and appreciate how much effort the community has put in.

After a new forum thread was posted, with the title Spikes Again by Wim van der Vegt is seemed like time to mount a proper investigation at mbed HQ.

I took up the challenge on the hardware side, with Emilio looking into the software implications.

The first thing we identified that was there was no single way of characterizing the ADC performance, everyone who has done experimentation around this have made their own (perfectly valid) way of measuring. We gave this some thought, and decided to come up with an ADC performance measuring program that we could publish - anyone who wants to do ADC work can reuse this, so we're all working against the same measurement.

The program is fairly simple, the main features are :

  • pin 17 is biased to 1.65v, 0.5 when reading the AnalogIn
  • It takes 500,000 samples and averages them, rejecting <0.45 and >0.55
  • with an average calculated, it takes samples in blocks of 500,000
  • Each sample is compared with the average, and counter is incremented according to the deviation
  • We are checking for 4,8,16,32,64,128,256,512,1024 and 2048 Least Significant Bit (LSB) differences
  • At the end of a 500,00 sample block, the running totals are displayed

Import programADCPerformanceMeter

Added explicit check for spikes

First Samples

The first step was to get an idea of the basic performance. The test conditions were:

  • mbed in an bread board
  • Pin 17 biased with a 10k potentiometer to 1.65v
  • The published program compiled and run as published
/media/uploads/chris/small_320_240_adcfirstrun.png
Test Harware

The results are pretty dire, especially the number of spike over 1024 LSB, which is 25% of the full scale

Taking an average over 500000 samples
Average = 0.503725
Profiling 2500000 samples
500000  337469  157284  572     2       2       0       3       15      23      0       15.900089
1000000 675545  313872  1118    3       3       0       5       32      45      0       15.901036
1500000 1014321 469761  1694    11      5       0       8       51      68      0       15.901663
2000000 1352535 626349  2244    12      8       0       12      68      87      0       15.900941
2500000 1690494 782901  2789    16      11      0       14      85      117     0       15.901027
==== Test Complete ====

NXP App note

While I was doing some various investigation with copper tape ground planes (which didnt work for me) Emilio was looking into an App note about the ADC :

Emilio had downloaded and run the example program on an MCB1700, and seen very good results. The NXP program sampled 10 values either side of the expected value, and also kept a counter of how many times a sample was seen outside this range. Its performance around the expected value was great.

Emilio then modified the program so that it was sampling wider, as our test program does, and still found the the MCB1700 to perform better than the mbed board (plugged into a breadboard). As it is the same silicon for the MCU, there are clearly some key differences in implementation to account for the differences.

First quick experiments

Firstly, I just want to do some quick experiments, going on hunches and circuit design intuition

10k potentiometer, other ADC pins grounded

Thinking about hardware influences that might affect the performance, I remembered that in the early days of my mbed experimentation, I found that driving an AnalogIn beyond 3.3v doesn't damage it, but can have an effect readings taken on other AnalogIn pins that are in the 0.0-3.3v range.

What if the other AnalogIn pins are introducing noise? Uninitialized pins are inputs with weak pulls ups.

So the next experiment was to pull all the other pins to ground.

Taking an average over 500000 samples
Average = 0.503577
Profiling 2500000 samples
500000  29474   470484  0       0       0       0       0       0       0       0       15.525647
1000000 60216   939693  0       0       0       0       0       0       0       0       15.527260
1500000 91377   1408476 0       0       0       0       0       0       0       0       15.527783
2000000 122109  1877696 0       0       0       0       0       0       0       0       15.527227
2500000 152269  2347477 0       0       0       0       0       0       0       0       15.526503
==== Test Complete ====

So that is a big improvement on the large spikes, but there is still quite a lot of low level noise.

10k potentiometer, other ADC pins driven low internally

My attention now turns to the questions "what if the ground wires are antenna?" - Can i simply declare the other ADC pins as DigitalOuts and drive them low?

Taking an average over 500000 samples
Average = 0.503498
Profiling 2500000 samples
500000  22149   477828  0       0       0       0       0       0       0       0       15.583364
1000000 46111   953845  0       0       0       0       0       0       0       0       15.585538
1500000 70468   1429466 0       0       0       0       0       0       0       0       15.586010
2000000 94537   1905376 0       0       0       0       0       0       0       0       15.585663
2500000 117597  2382290 0       0       0       0       0       0       0       0       15.584459
==== Test Complete ====

Not really that much better.

Fixed resistors, other ADC pins driven low internally

Going on a hunch, I started thinking how noisy a cheap potentiometer is, and how it's impedance might affect the noise level. So I decide to replace it with a pair of 4k7 resistors, which should give me roughly the same bias (given tolerances, and pot precision). Lets see how that goes.

Taking an average over 500000 samples
Average = 0.508731
Profiling 2500000 samples
500000  0       0       0       0       0       0       0       0       0       0       16.142586
1000000 0       0       0       0       0       0       0       0       0       0       16.142324
1500000 0       0       0       0       0       0       0       0       0       0       16.142078
2000000 0       0       0       0       0       0       0       0       0       0       16.141958
2500000 0       0       0       0       0       0       0       0       0       0       16.142168
==== Test Complete ====

Well, that looks pretty good :-)

Fixed resistors, other ADC pins floating

So now we have established that it *can* be made clean, lets see what the other factors might be.

Firstly, I'll repeat the fixed resistor experiment, but without grounding or driving the other ADC pins low.

Taking an average over 500000 samples
Average = 0.510484
Profiling 2500000 samples
500000  89      17      0       0       5       0       0       17      19      0       16.076658
1000000 185     40      1       1       7       0       5       36      38      0       16.076626
1500000 272     57      1       4       11      0       7       49      56      0       16.076687
2000000 346     71      4       5       17      0       9       62      77      0       16.076639
2500000 437     93      7       6       21      0       13      73      93      0       16.076696
==== Test Complete ====

The difference between grounded and floating pins with fixed value resistors is pretty big. One scenario is perfect, the other is pretty dire, so clearly, the state of the other pins has a big influence.

Now to experiment with a slightly more expensive "control" potentiometer, rather than the cheap trimmer. After all, connecting a potentiometer is a pretty standard thing to want to do!

Quality control potentiometer, other ADC pins grounded

When I tried this configuration with the low cost pot, the broad noise was missing, but there was a lot of low amplitude noise, which I believe it associated with the potentiometer.

/media/uploads/chris/small_320_240_qpot.jpg
'Quality Pot'

Taking an average over 500000 samples
Average = 0.535003
Profiling 2500000 samples
500000  284     496610  2959    19      4       0       2       3       2       0       15.551474
1000000 499     993821  5458    26      5       0       2       6       4       0       15.551894
1500000 839     1489438 9288    74      8       0       5       14      5       0       15.550431
2000000 1825    1977177 19734   252     34      0       14      64      41      0       15.541887
2500000 2688    2461966 33259   430     88      0       22      106     70      0       15.537833
==== Test Complete ====

Hmm... Maybe its not all that great quality after all.

50k potentiometer, other ADC pins grounded

This time I will use a 50k trimmer pot with the other ADC pins grounded.

/media/uploads/chris/small_320_240_trimmer50k.jpg
50k Trimmer configuration

Taking an average over 500000 samples
Average = 0.496764
Profiling 2500000 samples
500000  0       0       0       0       0       0       0       0       0       0       16.534067
1000000 0       0       0       0       0       0       0       0       0       0       16.534651
1500000 0       0       0       0       0       0       0       0       0       0       16.534674
2000000 0       0       0       0       0       0       0       0       0       0       16.534210
2500000 0       0       0       0       0       0       0       0       0       0       16.534685
==== Test Complete ====

I wasn't expecting that! This pot is very low profile, so maybe that is less opportunity to pick up noise.

'Quality' 10k potentiometer, other ADC pins grounded (again)

I'll repeat the test, but with really short leads to the pot, to test the hypothesis that the wires might be to blame.

Taking an average over 500000 samples
Average = 0.519472
Profiling 2500000 samples
500000  1476    496863  1130    117     4       0       8       36      24      0       15.554942
1000000 2400    995117  1744    160     7       0       10      48      28      0       15.555830
1500000 3147    1494181 1933    160     7       0       10      48      28      0       15.556872
2000000 4012    1993156 2093    160     7       0       10      48      28      0       15.557085
2500000 4893    2492121 2247    160     7       0       10      48      28      0       15.557070
==== Test Complete ====

So it really might be the pot rather than the wires.

Other Experiments

The App note from NXP suggests that a lot of noise can be introduced by a debug session taking place, because of the extra activity from the debug logic.

As the LPC1768 is *always* in debug, that might be a source of noise. I'll now rebuild the binary with floating inputs but have the mbed detach the debugger using the mbed_interface_disconnect(); function.

I'll run this test with floating inputs and the original potentiometer so see if there is a marked difference.

Potentiometer, floating ADC pins, no interface

Taking an average over 500000 samples
Average = 0.500203
Profiling 2500000 samples
500000  149419  1073    235     2       1       0       2       5       11      0       16.135899
1000000 298635  2105    450     4       2       0       3       12      24      0       16.135973
1500000 447184  3199    680     4       2       0       7       17      38      0       16.135851
2000000 595798  4249    883     4       3       0       8       26      51      0       16.135960
2500000 744252  5276    1106    4       4       0       10      33      69      0       16.135889
==== Test Complete ====

This compares favorably!

connected    : 1690494 782901  2789    16      11      0       14      85      117     0
disconnected : 744252  5276    1106    4       4       0       10      33      69      0

So what if we disconnect the interface, and drive the ADC pins low, using the original potentiometer.

Potentiometer, ADC pins driven low, no interface

Taking an average over 500000 samples
Average = 0.500188
Profiling 2500000 samples
500000  108293  7       0       0       0       0       0       0       0       0       16.209215
1000000 216290  22      0       0       0       0       0       0       0       0       16.209204
1500000 323363  27      0       0       0       0       0       0       0       0       16.209171
2000000 430649  31      0       0       0       0       0       0       0       0       16.209175
2500000 537710  38      0       0       0       0       0       0       0       0       16.209183
==== Test Complete ====

The noise has broadly gone. The only noise that remains is the low amplitude noise which I would attribute to the potentiometer.

Fixed 4k7 resistors, ADC pins driven low, no interface

aking an average over 500000 samples
Average = 0.508413
Profiling 2500000 samples
500000  0       0       0       0       0       0       0       0       0       0       16.207121
1000000 1       0       0       0       0       0       0       0       0       0       16.207090
1500000 2       0       0       0       0       0       0       0       0       0       16.207085
2000000 2       0       0       0       0       0       0       0       0       0       16.207071
2500000 3       0       0       0       0       0       0       0       0       0       16.207073
==== Test Complete ====

This isn't the "perfect zero's that we saw in the same configuration with the interface connected. The results are still good though, just three samples out of 2,500,000 have an error, but all those errors are less than 8 LSB

Note

Disabling the debug communucation between the interface and the LPC1768 reduces the digital activity inside the LPC1768 which can influence the noise on the ADC. The profile of the noise remains largely the same, but with the total number of errant samples in each range reduced significantly.

However, it should be done with careful consideration, as there is an impact on how the mbed behaves.


14 comments on Getting the best ADC performance from mbed:

05 Dec 2011

What exactly does "disabling the debug communication" do? What is the "impact on how the mbed behaves"?

Thanks for an excellent read.

06 Dec 2011

Hi Matt,

I'd moved all the mentions os the debug communication, as I was trying to make it as small an issue as possible :-)

On an out-of-the-box mbed, the LPC1768 is in a permanent debug session, so that the interface can poll the LPC1768 for any debug request. These requests include all the "semi-hosting" calls that enable the LPC1768 to do things like access the file system (LocalFileSystem) and the fetching of the MAC address from the interface.

When debug communication is switched off, these services silently disappear and accesses to them are most likely to cause the LPC1768 to hang in an infinite loop.

This is why disabling the interface without considering what is being taken away is very much discouraged, and why I was trying to sideline it a bit :-)

Interestingly, it would appear that driving the other ADC pins and providing a good quality signal (through design best practices) is probably enough, and certainly a good trade for keeping the system configuration the same.

Cheers, Chris

06 Dec 2011

I think the low-amplitude noise is coming from carbon as a resistive element. According to this: http://www.eetimes.com/design/automotive-design/4009614/Strategies-for-minimizing-resistor-generated-noise you can expect metal film resistors to have >20 dB less noise than carbon resistors. I think both the larger pots are probably using carbon as the resistive element (open one and look?). But I have no idea what the trimpot would use if not carbon...

16 Dec 2011

Have you thought about putting a small cap to ground to reduce the input impedance of the input signal??

CS

23 Dec 2011

Hi Chris

There is a part of the lpc manual you've missed in your experiments:

Quote:

29.6.3 Accuracy vs. digital receiver The ADC function must be selected via the PINSEL registers in order to get accurate voltage readings on the monitored pin. The PINMODE should also be set to the mode for which neither pull-up nor pull-down resistor is enabled. For a pin hosting an ADC input, it is not possible to have a have a digital function selected and yet get valid ADC readings. An inside circuit disconnects ADC hardware from the associated pin whenever a digital function is selected on that pin.

It seems like you have to disable the internal pull-up/down resitors too using the pinmode registers.

Btw can you add the PinMode to the DigitalOut class? It saves much low level access and figuring out the correct pin numbers.

wvd_vegt

10 Apr 2012

curry stomper wrote:

Have you thought about putting a small cap to ground to reduce the input impedance of the input signal??

try with 1nF very close to adc input so that it reduces impedance to ground path for noise signals above the

cutting frequency of the low pass filter formed by potentiometer, condenser and equivalent series resistance

of ADC (SAR type?) wich should be given in datasheet.

Good luck

Max

02 May 2012

.

24 Jun 2012

How are you "exactly" grounding other ADC pins? I can't seem to get near your perfect zero that way.

Using 4k7 resistors in a voltage divider trying to reproduce your experiment "Fixed resistors, other ADC pins driven low internally". I'm declareing them as DigitalOut and tying them to 0.

Taking an average over 500000 samples Average = 0.499930 Profiling 2500000 samples Samples 4 8 16 32 64 128 256 512 1024 Spikes Time 500000 108 3 0 0 0 0 0 0 0 0 16.677990s 1000000 205 7 0 0 0 0 0 1 0 0 16.678011s 1500000 305 11 0 0 0 0 0 1 0 0 16.678082s 2000000 420 11 0 0 0 0 0 1 0 0 16.678064s 2500000 517 16 0 0 0 0 0 2 0 0 16.677980s

Test Complete

24 Jun 2012

From my own experience, I found the following gave good results:-

Make sure you have a ground plane under the mbed connected to 0V.

Have good decoupling on the Vin very close to the module with several ceramics and a tantalum. Similarly decouple the 3V3 out if used.

For the analogue voltage to be measured take the 0V directly to the 0V mbed pin (i.e. it becomes a star point).

For the +ve analogue signal to be measured take it via a resistor to Ain and keep the connection short.

Also place a capacitor from Ain (connection kept short) and other side of the capacitor direct to the star point formed at the mbed 0V.

Choose the above R and C to match your input signal frequency spectrum.

On the software front, if you have the time available take multiple measurements and average the result. For my system I needed a result every 0.1seconds, so I took 25 readings at each point with a 10uS delay between each reading and used the average. This effectively samples the input at random points across the noise and I found that this gave sub-bit resolution.

I also found that the USB lead caused interferrence which could be reduced (but not eliminated) with a big ferrite clamped to the lead. It could also be reduced by making sure that the laptop was powered by an earthed supply, i.e. not a floating version. In getting to this point I also tried ferrite beads at the analogue input, but in my experience this did not give any benefit.

Removing any test equipment such as an oscilloscope also reduced the noise!

The biggest contribution came from the 0V star point, with good mbed decoupling.

I hope this helps.

Regarding your comment "other ADC pins driven low internally. I'm declareing them as DigitalOut and tying them to 0." I do not recommend tying them to 0V's, apart from the risk of a short circuit it could also introduce a ground loop of some sort thus increasing the noise.

Should you move on to using the DAC, then my advice is don't! It is horrendously noisy and I found it seems to increase the Ain noise. Use an SPI DAC if needed.

25 Jun 2012

I would recomend a MEDIAN filter not an average,

because an average filter will still contain effects from Hi & Lo spikes ..

have a look at these 'random' numbers I created;

Quote:

ADC...............Av.................Median

1 4934

2 4997

3 5074

4 5017

5 5000.........4705.888889.........5000

6 4984

7 5207

8 1948

9 5192

the Average number is a shocking 6.9% error

Which if you are attempting to predict End of charge in an LiPo cell could be disasterus !!

Ceri

25 Jun 2012

Hi Ceri,

Do you have a code example for your median filter that you'd be happy to share?

Apologies if you've already posted one (I thought you had but couldn't find the one I was thinking of when I had a look last night)...

Thanks, Jez

26 Jun 2012

Just somthing simple,

I do recomend you test it though .. you could also put a second loop to save lots of typing


// Array contains 9 NEW samples, 0..8, 

for (x=0;x<9;x++)
{
if (Array[8]>Array[9])
{
// Swap 
temp = Array[9];
Array[8] = Array[9];
Array [8] = temp;
}
//--
if (Array[7]>Array[8])
{
// Swap 
temp = Array[8];
Array[7] = Array[8];
Array [7] = temp;
}
//--
if (Array[6]>Array[7])
{
// Swap 
temp = Array[7];
Array[6] = Array[7];
Array [6] = temp;
}
....
...
..
.
}


result is in Array[4] Assuming 9 cells

This should work, but i have kust scribbled it down :)

Basicaly the code 'BUBBLE' sorts the 9 values, swapping each piar repeatedly,

..

Annother method is to scan through find the higest number, (saving place)

put into first cell of a NEW Array, set the source to ZERO (or MAX neg if +/-)

Repeat for number of cells.

the second method does need twice the memory, not good for small micros.

Hope this helps

Ceri

08 Apr 2016

Only four years late to the party.

I'm going to be using log slider pots for a project and after reading this thread I did an experiment today.

First I put a rotary 10k pot into a breadboard, hooked up a digital voltmeter to get as close to 1.65v as possible and ran ADCPerformanceMeter. The results were similar to the first test above but not quite as dire.

Next I removed the voltmeter. The results were similar to the first test but definately an improvement.

Finally I added a 1n0K100 capacitor as suggested by Curry and Massimo above. zeroes right across the board except for the first '4' column with 3 at 500000 going up to 8 at 2500000. I'll be able to live with that if there's the same results for my slide pots.

I took a lovely photo of the breadboard but can't figure out how to get it on here.

Paul.

27 Apr 2016

I am planning to use the LPC1768 as a data logger. ADCPerformanceMeter was really useful to help quieten the board down. Unfortunately when I switched to my program which uses a 1mS ticker to read the data and then stores the info on an SDCard with the aid of a circular buffer it started getting pretty noisy.

I will get the 'scope out - but might have to switch to an external ADC.

-Jamie

Please log in to post comments.